读 SDWebImage 六 (编码器四:SDWebImageImageIOCoder)
私有静态常量
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
| //每像素字节数为4 static const size_t kBytesPerPixel = 4; //每位字节数为8 static const size_t kBitsPerComponent = 8;
/* *设置标志“SDWebImageScaleDownLargeImages”时,解码图像的最大大小(以MB为单位) * iPad1和iPhone 3GS的建议值:60。 * iPad2和iPhone 4的建议值:120。 * iPhone 3G和iPod 2及早期设备的建议值:30。 */ static const CGFloat kDestImageSizeMB = 60.0f;
/* *当设置标志“SDWebImageScaleDownLargeImages”时,定义用于解码图像的图块的最大大小(以MB为单位) * iPad1和iPhone 3GS的建议值:20。 * iPad2和iPhone 4的建议值:40。 * iPhone 3G和iPod 2及早期设备的建议值:10。 */ static const CGFloat kSourceImageTileSizeMB = 20.0f;
//定义 1M = 1024.0f * 1024.0f static const CGFloat kBytesPerMB = 1024.0f * 1024.0f;
//定义1MB的像素数是1MB的Bytes数量除以1像素的Bytes数 static const CGFloat kPixelsPerMB = kBytesPerMB / kBytesPerPixel; //定义解码完的图像的最大像素总数数是解码完的图像最size乘以1MB的像素数 static const CGFloat kDestTotalPixels = kDestImageSizeMB * kPixelsPerMB;
//定义用于解码的图像的最大像素总数是解码的图像的size乘以1MB的像素数 static const CGFloat kTileTotalPixels = kSourceImageTileSizeMB * kPixelsPerMB;
//定义重叠像素大小为2像素 static const CGFloat kDestSeemOverlap = 2.0f;
|
私有变量
1 2 3 4 5 6 7 8
| //定义了两个变量来保存图像的宽和高 size_t _width, _height; #if SD_UIKIT || SD_WATCH //定义变量记录图片方向 UIImageOrientation _orientation; #endif //定义变量记录图片源 CGImageSourceRef _imageSource;
|
life cycle
单例方法和dealloc方法
C 语言不能够调用OC中的retain与release,一般的C 语言接口都提供了release函数(比如CGContextRelease(context c))来管理内存。ARC不会自动调用这些C接口的函数,所以这还是需要我们自己来进行管理的.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| - (void)dealloc { //如果CGImageSourceRef类型的_imageSource存在,在dealloc方法中手动释放掉(CGXXX 和CFXXX方法,ARC并不会自动释放,需要手动去释放掉) if (_imageSource) { CFRelease(_imageSource); _imageSource = NULL; } }
//单例方法 + (instancetype)sharedCoder { static SDWebImageImageIOCoder *coder; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ coder = [[SDWebImageImageIOCoder alloc] init]; }); return coder; }
|
解码
SDWebImageCoder
协议方法
判断是否支持图片数据的解码
1 2 3 4 5 6 7 8 9 10 11 12 13
| - (BOOL)canDecodeFromData:(nullable NSData *)data { //通过NSData中的扩展方法:sd_imageFormatForImageData 获取图片的格式 switch ([NSData sd_imageFormatForImageData:data]) { case SDImageFormatWebP: // 不支持WebP格式图片解码 return NO; case SDImageFormatHEIC: // 检查是否支持HEIC格式图片解码 return [[self class] canDecodeFromHEICFormat]; default: return YES; } }
|
通过图片数据(ImageData)解码获取图片
1 2 3 4 5 6 7 8 9 10 11 12 13
| - (UIImage *)decodedImageWithData:(NSData *)data { //如果data为空,返回nil if (!data) { return nil; } //将imagedata转为图片 UIImage *image = [[UIImage alloc] initWithData:data]; //设置图片的格式 image.sd_imageFormat = [NSData sd_imageFormatForImageData:data]; 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 46 47 48 49 50 51 52 53 54 55 56 57 58
| /** 使用原始图片和图片数据解压缩图片。
@param image 需要压缩的原图片 @param data 指向原始图像数据的指针。 指针本身是非空的,但图像数据可以为空。 如果需要,此数据将设置为缓存。 如果您不需要同时修改数据,请忽略此参数。 @param optionsDict 一个包含任何解压缩选项的字典。 通过{SDWebImageCoderScaleDownLargeImagesKey:@(YES)}缩小大图像 @return 解压缩后的图片 */ - (UIImage *)decompressedImageWithImage:(UIImage *)image data:(NSData *__autoreleasing _Nullable *)data options:(nullable NSDictionary<NSString*, NSObject*>*)optionsDict { //如果是mac,直接返回图片 #if SD_MAC return image; #endif
//如果是iOS 、osTV、osWatch #if SD_UIKIT || SD_WATCH // 创建变量保存,默认是不缩小 BOOL shouldScaleDown = NO; // 如果传入选项字典参数 if (optionsDict != nil) { //创建临时变量保存选项值 NSNumber *scaleDownLargeImagesOption = nil; //SDWebImageCoderScaleDownLargeImagesKey对应的value类型为NSNumber,则保存value if ([optionsDict[SDWebImageCoderScaleDownLargeImagesKey] isKindOfClass:[NSNumber class]]) { scaleDownLargeImagesOption = (NSNumber *)optionsDict[SDWebImageCoderScaleDownLargeImagesKey]; } //如果选项值不为nil,转换成BOOL类型,并保存 if (scaleDownLargeImagesOption != nil) { shouldScaleDown = [scaleDownLargeImagesOption boolValue]; } } //如果不需要缩小图片,直接执行解压图片操作 if (!shouldScaleDown) { return [self sd_decompressedImageWithImage:image]; } else { 如果需要缩小图片,则执行图片的解压和缩小操作 UIImage *scaledDownImage = [self sd_decompressedAndScaledDownImageWithImage:image]; // 如果解压和缩小成功,仍处理数据指针,调用压缩方法 if (scaledDownImage && !CGSizeEqualToSize(scaledDownImage.size, image.size)) { //根据图片数据指针得到图片的格式 SDImageFormat format = [NSData sd_imageFormatForImageData:*data]; //通过缩小的图片和图片格式编码获取imagedata NSData *imageData = [self encodedDataWithImage:scaledDownImage format:format]; if (imageData) { // 通过参数回传处理后的图像数据 *data = imageData; } } // 返回处理后的图片 return scaledDownImage; } #endif }
|
SDWebImageProgressiveCoder
协议方法
判断是否支持增量解码某个data数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| - (BOOL)canIncrementallyDecodeFromData:(NSData *)data { //获取图片的格式 switch ([NSData sd_imageFormatForImageData:data]) { case SDImageFormatWebP: // 不支持WebP逐行解码 return NO; case SDImageFormatHEIC: // 检查HEIC解码兼容性 return [[self class] canDecodeFromHEICFormat]; default: // 其他类型就返回YES return YES; } }
|
将图片数据增量解码为图片
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
| - (UIImage *)incrementallyDecodedImageWithData:(NSData *)data finished:(BOOL)finished { 判断增量图片源是否存在,如果不存在,创建一个新的 if (!_imageSource) { _imageSource = CGImageSourceCreateIncremental(NULL); } UIImage *image; //更新数据源,这里必须传递所有数据,而不仅仅是新字节 CGImageSourceUpdateData(_imageSource, (__bridge CFDataRef)data, finished); // 获取到图像的宽、高和方向 if (_width + _height == 0) { CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(_imageSource, 0, NULL); if (properties) { NSInteger orientationValue = 1; CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight); if (val) CFNumberGetValue(val, kCFNumberLongType, &_height); val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth); if (val) CFNumberGetValue(val, kCFNumberLongType, &_width); val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation); if (val) CFNumberGetValue(val, kCFNumberNSIntegerType, &orientationValue); CFRelease(properties); //当我们绘制到Core Graphics时,我们会丢失方向信息,这意味着initWithCGIImage生成的图像有时会导致错误定向。 (与didCompleteWithError中的initWithData生成的图像不同。)因此将其保存在此处并稍后传递。 #if SD_UIKIT || SD_WATCH _orientation = [SDWebImageCoderHelper imageOrientationFromEXIFOrientation:orientationValue]; #endif } } if (_width + _height > 0) { // 创建位图对象 CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(_imageSource, 0, NULL); //如果存在,则根据该位图生成图片 if (partialImageRef) { #if SD_UIKIT || SD_WATCH image = [[UIImage alloc] initWithCGImage:partialImageRef scale:1 orientation:_orientation]; #elif SD_MAC image = [[UIImage alloc] initWithCGImage:partialImageRef size:NSZeroSize]; #endif // 位图对象的引用计数-1 CGImageRelease(partialImageRef); image.sd_imageFormat = [NSData sd_imageFormatForImageData:data]; } } // 如果加载完成就释放掉位图对象,并置空 if (finished) { if (_imageSource) { CFRelease(_imageSource); _imageSource = NULL; } } //返回该图片 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 46 47 48 49 50 51 52 53 54 55 56
| #if SD_UIKIT || SD_WATCH - (nullable UIImage *)sd_decompressedImageWithImage:(nullable UIImage *)image {
//如果图片不需要解码,直接返回image if (![[self class] shouldDecodeImage:image]) { return image; } //自动释放位图上下文和所有变量以帮助系统在存在内存警告时释放内存。 //在iOS7上,别忘了调用[[SDImageCache sharedImageCache] clearMemory]; // 建立自动释放池,以帮助系统在收到内存警告时释放内存。 @autoreleasepool{ //获取图片对象的位图图片 CGImageRef imageRef = image.CGImage; // 获取图片对象的色彩空间 CGColorSpaceRef colorspaceRef = SDCGColorSpaceGetDeviceRGB(); BOOL hasAlpha = SDCGImageRefContainsAlpha(imageRef); // iOS 显示 Alpha(透明)信息 (BRGA8888/BGRX8888) CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host; bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst; size_t width = CGImageGetWidth(imageRef); size_t height = CGImageGetHeight(imageRef); // CGBitmapContextCreate不支持kCGImageAlphaNone。由于此处的原始图像没有alpha信息,因此使用kCGImageAlphaNoneSkipLast创建没有alpha信息的位图图形上下文。 //创建位图图形上下文 CGContextRef context = CGBitmapContextCreate(NULL, width, height, kBitsPerComponent, 0, colorspaceRef, bitmapInfo); //如果创建失败,直接返回image if (context == NULL) { return image; } // 将图像绘制到上下文中并检索没有alpha的新位图图像 CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); // 生成位图图像 CGImageRef imageRefWithoutAlpha = CGBitmapContextCreateImage(context); // 根据位图图像生成图片对象 UIImage *imageWithoutAlpha = [[UIImage alloc] initWithCGImage:imageRefWithoutAlpha scale:image.scale orientation:image.imageOrientation]; //释放掉上下文 CGContextRelease(context); //释放掉位图图片 CGImageRelease(imageRefWithoutAlpha); //返回位图图片 return imageWithoutAlpha; } }
|
//解压并按比例缩小图片
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 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
| - (nullable UIImage *)sd_decompressedAndScaledDownImageWithImage:(nullable UIImage *)image { //如果图片不需要解码,直接返回image if (![[self class] shouldDecodeImage:image]) { return image; } //如果图片不需要缩小,直接执行解压图片操作 if (![[self class] shouldScaleDownImage:image]) { return [self sd_decompressedImageWithImage:image]; } // 创建图像上下文 CGContextRef destContext; //自动释放位图上下文和所有变量以帮助系统在存在内存警告时释放内存。 //在iOS7上,别忘了调用[[SDImageCache sharedImageCache] clearMemory]; // 建立自动释放池,以帮助系统在收到内存警告时释放内存 @autoreleasepool { //获取图片对象的位图图片 CGImageRef sourceImageRef = image.CGImage; // 获取图像的总像素数 CGSize sourceResolution = CGSizeZero; sourceResolution.width = CGImageGetWidth(sourceImageRef); sourceResolution.height = CGImageGetHeight(sourceImageRef); float sourceTotalPixels = sourceResolution.width * sourceResolution.height; // 计算缩小比例 float imageScale = kDestTotalPixels / sourceTotalPixels; // 计算缩小后的尺寸 CGSize destResolution = CGSizeZero; destResolution.width = (int)(sourceResolution.width*imageScale); destResolution.height = (int)(sourceResolution.height*imageScale); // 获取色彩空间 CGColorSpaceRef colorspaceRef = SDCGColorSpaceGetDeviceRGB(); BOOL hasAlpha = SDCGImageRefContainsAlpha(sourceImageRef); // iOS 显示 Alpha(透明)信息 (BGRA8888/BGRX8888) CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host; bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst; // CGBitmapContextCreate不支持kCGImageAlphaNone。由于此处的原始图像没有alpha信息,因此使用kCGImageAlphaNoneSkipLast创建没有alpha信息的位图图形上下文。 //创建位图图形上下文 destContext = CGBitmapContextCreate(NULL, destResolution.width, destResolution.height, kBitsPerComponent, 0, colorspaceRef, bitmapInfo); //如果创建失败,直接返回image if (destContext == NULL) { return image; } // 设置图像上下文的绘图质量 CGContextSetInterpolationQuality(destContext, kCGInterpolationHigh); //现在定义用于从输入图像到输出图像的增量blit的矩形的大小。由于iOS从磁盘检索图像数据的方式,我们使用的源块宽度等于源图像的宽度。 iOS必须以全宽“波段”从磁盘解码图像,即使当前图形上下文被剪切为该波段内的子图形也是如此。 因此,我们通过将我们的图块大小设置为输入图像的整个宽度来充分利用由解码操作产生的所有像素数据。 // 创建临时变量保存来源块大小 CGRect sourceTile = CGRectZero; // 来源块的宽度就是原图的宽度 sourceTile.size.width = sourceResolution.width; // 源图块高度是动态的。 由于我们以MB为单位指定了源块的大小,因此请查看输入图像宽度可以为多少像素行。 // 计算来源块的高度 sourceTile.size.height = (int)(kTileTotalPixels / sourceTile.size.width ); // 设置来源块的X坐标 sourceTile.origin.x = 0.0f; //输出图块与输入图块的比例相同,但缩放为图像比例。 // 创建历史变量保存目标块大小 CGRect destTile; // 目标块的宽是缩放后的宽 destTile.size.width = destResolution.width; // 目标块的宽是来源块的高乘以缩放比 destTile.size.height = sourceTile.size.height * imageScale; // 设置目标块的X坐标 destTile.origin.x = 0.0f; //来源块与目标似乎重叠成比例。 这是我们组装输出图像时每个图块重叠的像素数量。 // 计算来源块与目标块的重复区域 float sourceSeemOverlap = (int)((kDestSeemOverlap/destResolution.height)*sourceResolution.height); // 生成变量保存来源块图像位图 CGImageRef sourceTileImageRef; // 计算组装输出图像所需的读/写操作次数。 // 计算需要绘制的次数 int iterations = (int)( sourceResolution.height / sourceTile.size.height ); // 如果图块高度未均匀划分图像高度,请添加另一个迭代以考虑剩余像素。 // 计算剩余像素的高度 int remainder = (int)sourceResolution.height % (int)sourceTile.size.height; // 如果有剩余像素就将绘制次数加1 if(remainder) { iterations++; } // 添加似乎与切片重叠,但保存原始切片高度以进行y坐标计算。 // 创建变量保存来源块的高度,用来计算纵坐标的移动 float sourceTileHeightMinusOverlap = sourceTile.size.height; // 来源块高度加上要重复覆盖的高度 sourceTile.size.height += sourceSeemOverlap; // 目标块高度加上重叠的像素数 destTile.size.height += kDestSeemOverlap; // 开启循环绘制图像 for( int y = 0; y < iterations; ++y ) { // 建立自动释放池,以帮助系统在收到内存警告时释放内存。 @autoreleasepool { // 计算来源块的纵坐标:来源块的高度乘以当前循环次数,然后加上重复覆盖的高度 sourceTile.origin.y = y * sourceTileHeightMinusOverlap + sourceSeemOverlap; // 计算目标块的纵坐标:目标图像的高度减去要绘制的来源块的高度乘以压缩比,再减去重叠高度 // 这个地方,来源块的纵坐标是递增的,目标块的纵坐标是递减的,这是因为为UIKit的坐标系和CGContext是镜像关系 destTile.origin.y = destResolution.height - (( y + 1 ) * sourceTileHeightMinusOverlap * imageScale + kDestSeemOverlap); // 按照计算好的尺寸绘制来源块的位图 sourceTileImageRef = CGImageCreateWithImageInRect( sourceImageRef, sourceTile ); // 如果是最后一块要绘制,并且这一块是剩余的像素 if( y == iterations - 1 && remainder ) { // 因为剩余像素的高度是不固定的,所以重新计算目标块的纵坐标 float dify = destTile.size.height; destTile.size.height = CGImageGetHeight( sourceTileImageRef ) * imageScale; dify -= destTile.size.height; destTile.origin.y += dify; } // 将来源块位图按照计算好的尺寸绘制到目标图像上下文中 CGContextDrawImage( destContext, destTile, sourceTileImageRef ); // 释放来源块位图 CGImageRelease( sourceTileImageRef ); } } // 根据目标图像上下文生成目标图像位图 CGImageRef destImageRef = CGBitmapContextCreateImage(destContext); // 释放目标图像上下文 CGContextRelease(destContext); // 如果生成位图失败就直接返回图片对象 if (destImageRef == NULL) { return image; } // 生成目标图片对象 UIImage *destImage = [[UIImage alloc] initWithCGImage:destImageRef scale:image.scale orientation:image.imageOrientation]; // 释放目标图像位图 CGImageRelease(destImageRef); // 如果生成图片对象失败就直接返回图片对象 if (destImage == nil) { return image; } // 返回目标图片对象 return destImage; } } #endif
|
编码
判断某个图片格式是否支持编码
1 2 3 4 5 6 7 8 9 10 11 12 13
| //如果是WebP格式,则不支持编码;HEIC格式:根据canEncodeToHEICFormat方法判断是否支持编码;其他格式都支持编码 - (BOOL)canEncodeToFormat:(SDImageFormat)format { switch (format) { case SDImageFormatWebP: // 不支持WebP格式编码 return NO; case SDImageFormatHEIC: // 检查HEIC编码兼容性 return [[self class] canEncodeToHEICFormat]; default: return YES; } }
|
将图片根据给定的格式解码成图片数据(imageData)
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
| - (NSData *)encodedDataWithImage:(UIImage *)image format:(SDImageFormat)format { //如果图片不存在,返回nil if (!image) { return nil; } //如果格式类型未识别,判断图片是否含有Alpha通道,如果有则定义为PNG格式,否则定义为JPEG格式 if (format == SDImageFormatUndefined) { BOOL hasAlpha = SDCGImageRefContainsAlpha(image.CGImage); if (hasAlpha) { format = SDImageFormatPNG; } else { format = SDImageFormatJPEG; } } NSMutableData *imageData = [NSMutableData data]; CFStringRef imageUTType = [NSData sd_UTTypeFromSDImageFormat:format]; // 根据imageData 和 CFStringRef 创建 CGImageDestinationRef对象,如果创建失败,返回nil CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, 1, NULL); if (!imageDestination) { // Handle failure. return nil; } //创建字典对象保存编码参数 NSMutableDictionary *properties = [NSMutableDictionary dictionary]; #if SD_UIKIT || SD_WATCH //// 获取图片的方向 NSInteger exifOrientation = [SDWebImageCoderHelper exifOrientationFromImageOrientation:image.imageOrientation]; //设置方向参数 [properties setValue:@(exifOrientation) forKey:(__bridge NSString *)kCGImagePropertyOrientation]; #endif // 添加图片位图对象到创建的CGImageDestinationRef对象中 CGImageDestinationAddImage(imageDestination, image.CGImage, (__bridge CFDictionaryRef)properties); // 如果编码失败就返回空 if (CGImageDestinationFinalize(imageDestination) == NO) { // Handle failure. imageData = nil; } //释放掉imageDestination对象 CFRelease(imageDestination); //返回 return [imageData copy]; }
|
私有方法(辅助方法)
- 是否需要解压图片
(如果图片为动图或不存在,不解压图片,否则解压图片)
1 2 3 4 5 6 7 8 9 10 11 12 13
| + (BOOL)shouldDecodeImage:(nullable UIImage *)image { //如果图片为空,返回NO if (image == nil) { return NO; } // 如果图片为动图,返回NO if (image.images != nil) { return NO; } 否则返回YES return YES; }
|
- 是否支持
HEIC
格式类型的图片解码
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
| + (BOOL)canDecodeFromHEICFormat { //临时静态变量 canDecode 为 NO static BOOL canDecode = NO; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //忽略-Wunguarded-availability警告 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunguarded-availability"
//如果是iWatch 或者 SIMULATOR ,则不支持 #if TARGET_OS_SIMULATOR || SD_WATCH //iWatch canDecode = NO; #elif SD_MAC //mac //获取进程信息对象 NSProcessInfo *processInfo = [NSProcessInfo processInfo]; if ([processInfo respondsToSelector:@selector(operatingSystemVersion)]) { //通过进程信息对象获取操作系统版本。判断macOS系统版本10.13+,则支持HEIC类型图像的解码 canDecode = processInfo.operatingSystemVersion.minorVersion >= 13; } else { canDecode = NO; } #elif SD_UIKIT //iOS 和 tvOS //获取进程信息对象 NSProcessInfo *processInfo = [NSProcessInfo processInfo]; if ([processInfo respondsToSelector:@selector(operatingSystemVersion)]) { //通过进程信息对象获取操作系统版本。判断操作系统版本是iOS 11+就支持HEIC类型图像的解码 canDecode = processInfo.operatingSystemVersion.majorVersion >= 11; } else { canDecode = NO; } #endif #pragma clang diagnostic pop }); return canDecode; }
|
- 是否支持
HEIC
格式类型的图片编码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| + (BOOL)canEncodeToHEICFormat { //临时静态变量 canDecode 为 NO static BOOL canEncode = NO; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSMutableData *imageData = [NSMutableData data]; CFStringRef imageUTType = [NSData sd_UTTypeFromSDImageFormat:SDImageFormatHEIC]; // 创建一个CGImageDestinationRef对象,如果该对象可以创建成功,则需要编码,否则不需要 CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, 1, NULL); if (!imageDestination) { // Can't encode to HEIC canEncode = NO; } else { // Can encode to HEIC CFRelease(imageDestination); canEncode = YES; } }); return canEncode; }
|
- 是否应该缩小图片
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
| #if SD_UIKIT || SD_WATCH + (BOOL)shouldScaleDownImage:(nonnull UIImage *)image { BOOL shouldScaleDown = YES; //获取图片的位图图像 CGImageRef sourceImageRef = image.CGImage; //获取到位图图像的宽和高 CGSize sourceResolution = CGSizeZero; sourceResolution.width = CGImageGetWidth(sourceImageRef); sourceResolution.height = CGImageGetHeight(sourceImageRef); //计算图片的总像素数 float sourceTotalPixels = sourceResolution.width * sourceResolution.height; //图片的压缩比: 最大像素数 / 图片的总像素数 float imageScale = kDestTotalPixels / sourceTotalPixels; //如果压缩比小于1,返回YES,否则返回NO if (imageScale < 1) { shouldScaleDown = YES; } else { shouldScaleDown = NO; } return shouldScaleDown; } #endif
|