近期工作不算忙,因此简单看了一下这个OC常用的图片解析下载类
前言
首先,我们应该知道,SDWebImage底层实现有沙盒缓存机制,主要由三块组成
1、内存图片缓存
2、内存操作缓存
3、磁盘沙盒缓存
NSData+ImageContentType
根据图片二进制数据判断图片类型
+ (NSString *)sd_contentTypeForImageData:(NSData *)data{
uint8_t c;
/*
* 常用的这几种格式图片的头部信息标识(十六进制)。
1.Png图片文件包括8字节:89 50 4E 47 0D 0A 1A 0A。即为 .PNG....。
2.Jpg图片文件包括2字节:FF D8。
3.Gif图片文件包括6字节:47 49 46 38 39|37 61 。即为 GIF89(7)a。
4.Bmp图片文件包括2字节:42 4D。即为 BM。
*/
//获取每一个字符
[data getBytes:&c length:1];
switch (c) {
case 0xFF:
return @"image/jpeg";
case 0x89:
return @"image/png";
case 0x47:
return @"image/gif";
case 0x49:
case 0x4D:
return @"image/tiff";
case 0x52:
// R as RIFF for WEBP
if ([data length] < 12) {
return nil;
}
NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
return @"image/webp";
}
return nil;
}
return nil;
}
SDImageCache
图片缓存类, 根据key进行图片缓存(内存/磁盘),根据key从缓存中
取出图片,以及删除图片,清除所有缓存等.
SDWebImageCompat
- 线程操作安全
同步操作
#define dispatch_main_sync_safe(block)\
if ([NSThread isMainThread]) {\
block();\
} else {\
dispatch_sync(dispatch_get_main_queue(), block);\
}
异步操作
#define dispatch_main_async_safe(block)\
if ([NSThread isMainThread]) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}
用法
dispatch_main_async_safe(^{
执行代码
});
SDWebImageDecoder
alpha通道的解释:
1. Alpha 没有透明度的意思,不代表透明度。opacity 和 transparency 才和透明度有关,前者是不透明度,后者是透明度。比如 css 中的「opacity: 0.5」就是设定元素有 50% 的不透明度。
2. 一个图像的每个像素都有 RGB 三个通道,后来 [Alvy Ray Smith](http://en.wikipedia.org/wiki/Alvy_Ray_Smith) 提出每个像素再增加一个 Alpha 通道,取值为0到1,用来储存这个像素是否对图片有「贡献」,0代表透明、1代表不透明。也就是说,「Alpha 通道」储存一个值,其外在表现是「透明度」,Alpha 和透明度没啥关系。
3. 为什么取名为 Alpha 通道,我觉得是因为这是除RGB以外「第一个通道」的意思,没有别的更深刻的含义。
4. 「Alpha 通道」是图片内在的一个属性,用 css 或者其他外部方法设定透明度,并没有改变图片的 Alpha 通道的值。
5. 阿尔法通道(α Channel或Alpha Channel)是指一张图片的透明和半透明度。例如:一个使用每个像素16比特存储的位图,对于图形中的每一个像素而言,可能以5个比特表示红色,5个比特表示绿色,5个比特表示蓝色,最后一个比特是阿尔法。在这种情况下,它要么表示透明要么不是,因为阿尔法比特只有0或1两种不同表示的可能性。又如一个使用32个比特存储的位图,每8个比特表示红绿蓝,和阿尔法通道。在这种情况下,就不光可以表示透明还是不透明,阿尔法通道还可以表示256级的半透明度,因为阿尔法通道有8个比特可以有256种不同的数据表示可能性。
CGImageRef ,这个结构用来创建像素位图,可以通过操作存储的像素位来编辑图片。
CGImageRef imageRef = image.CGImage;
typedef CF_ENUM(uint32_t, CGImageAlphaInfo) {
kCGImageAlphaNone, /* For example, RGB. */
kCGImageAlphaPremultipliedLast, /* For example, premultiplied RGBA */
kCGImageAlphaPremultipliedFirst, /* For example, premultiplied ARGB */
kCGImageAlphaLast, /* For example, non-premultiplied RGBA */
kCGImageAlphaFirst, /* For example, non-premultiplied ARGB */
kCGImageAlphaNoneSkipLast, /* For example, RBGX. */
kCGImageAlphaNoneSkipFirst, /* For example, XRGB. */
kCGImageAlphaOnly /* No color data, alpha data only */
};
/**
图片解码
传统的UIImage进行解码都是在主线程上进行的,比如
UIImage * image = [UIImage imageNamed:@"123.jpg"];
self.imageView.image = image;
在这个时候,图片其实并没有解码。而是,当图片实际需要显示到屏幕上的时候,CPU才会进行解码,绘制成纹理什么的,交给GPU渲染。这其实是很占用主线程CPU时间的,而众所周知,主线程的时间真的很宝贵
@param image 原图片
@return 解码后的图片
*/
+ (UIImage *)decodedImageWithImage:(UIImage *)image {
// while downloading huge amount of images
// autorelease the bitmap context
// and all vars to help system to free memory
// when there are memory warning.
// on iOS7, do not forget to call
// [[SDImageCache sharedImageCache] clearMemory];
if (image == nil) { // Prevent "CGBitmapContextCreateImage: invalid context 0x0" error
return nil;
}
@autoreleasepool{
// 如果是gif图片的话,直接返回
if (image.images != nil) {
return image;
}
//CGImageRef ,这个结构用来创建像素位图,可以通过操作存储的像素位来编辑图片。
CGImageRef imageRef = image.CGImage;
//图片是否包含alpha通道
CGImageAlphaInfo alpha = CGImageGetAlphaInfo(imageRef);
BOOL anyAlpha = (alpha == kCGImageAlphaFirst ||
alpha == kCGImageAlphaLast ||
alpha == kCGImageAlphaPremultipliedFirst ||
alpha == kCGImageAlphaPremultipliedLast);
if (anyAlpha) {
//有alpha通道,直接返回
return image;
}
//在IOS中,进过处理的图片数据会被保存在CGImage对象中,而8位图的调色板会被保存在CGImage的颜色空间CGColorSpace中
// current
CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(CGImageGetColorSpace(imageRef));
CGColorSpaceRef colorspaceRef = CGImageGetColorSpace(imageRef);
BOOL unsupportedColorSpace = (imageColorSpaceModel == kCGColorSpaceModelUnknown ||
imageColorSpaceModel == kCGColorSpaceModelMonochrome ||
imageColorSpaceModel == kCGColorSpaceModelCMYK ||
imageColorSpaceModel == kCGColorSpaceModelIndexed);
if (unsupportedColorSpace) {
//色彩空间:(Color Space)这是一个色彩范围的容器,类型必须是CGColorSpaceRef.对于这个参数,我们可以传入CGColorSpaceCreateDeviceRGB函数的返回值,它将给我们一个RGB色彩空间。
colorspaceRef = CGColorSpaceCreateDeviceRGB();
}
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
// kCGImageAlphaNone is not supported in CGBitmapContextCreate.
// Since the original image here has no alpha info, use kCGImageAlphaNoneSkipLast
// to create bitmap graphics contexts without alpha info.
//创建bitmapcontext
CGContextRef context = CGBitmapContextCreate(NULL,
width,
height,
bitsPerComponent,
bytesPerRow,
colorspaceRef,
kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast);
// Draw the image into the context and retrieve the new bitmap image without alpha
//回执image到context中,强制解码
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
//根据上下文获取解码后的图片
CGImageRef imageRefWithoutAlpha = CGBitmapContextCreateImage(context);
UIImage *imageWithoutAlpha = [UIImage imageWithCGImage:imageRefWithoutAlpha
scale:image.scale
orientation:image.imageOrientation];
if (unsupportedColorSpace) {
CGColorSpaceRelease(colorspaceRef);
}
CGContextRelease(context);
CGImageRelease(imageRefWithoutAlpha);
return imageWithoutAlpha;
}
}
clang diagnostic的使用
使用格式大致如下:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-相关命令"
//需要操作的代码
#pragma clang diagnostic pop
SDWebImageDownloader
NSString *oneImageURL =
@"http://wallpapers.wallbase.cc/rozne/wallpaper-573934.jpg";
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:[NSURL URLWithString:oneImageURL]
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize)
{
//此处为下载进度
}
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished)
{
//下载完成后进入这里执行
}];
UIImage+GIF
用于解析gif图片
/**
根据data解析出图片
@param data sata
@return 图片
*/
+ (UIImage *)sd_animatedGIFWithData:(NSData *)data {
if (!data) {
return nil;
}
//获取图片数据
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
//获取source里面图片数量
size_t count = CGImageSourceGetCount(source);
UIImage *animatedImage;
if (count <= 1) {//如果只有一张图,说明是静态图。
animatedImage = [[UIImage alloc] initWithData:data];
}
else {
NSMutableArray *images = [NSMutableArray array];
NSTimeInterval duration = 0.0f;
for (size_t i = 0; i < count; i++) {
//取出每一张图片
CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);
if (!image) {
continue;
}
//计算图片显示时间
duration += [self sd_frameDurationAtIndex:i source:source];
[images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]];
//释放图片资源
CGImageRelease(image);
}
if (!duration) {
duration = (1.0f / 10.0f) * count;
}
//动态图图片资源,以及显示时间。
animatedImage = [UIImage animatedImageWithImages:images duration:duration];
}
//释放
CFRelease(source);
return animatedImage;
}
UIImageView+WebCache
核心内容
用于解析图片展示在imageview上
/**
下载图片,展示在UIImageView上
@param url 图片url
@param placeholder 占位图
@param options 操作类型(枚举值)
@param progressBlock 下载进度回调
@param completedBlock 完成后的回调
*/
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {
//取消当前下载的图片
[self sd_cancelCurrentImageLoad];
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//设置显示占位图
if (!(options & SDWebImageDelayPlaceholder)) {
dispatch_main_async_safe(^{
self.image = placeholder;
});
}
if (url) {
// 选择是否显示加载进度
if ([self showActivityIndicatorView]) {
[self addActivityIndicator];
}
__weak __typeof(self)wself = self;
//下载图片
id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
//移除进度条
[wself removeActivityIndicator];
if (!wself) return;
dispatch_main_sync_safe(^{
if (!wself) return;
//SDWebImageAvoidAutoSetImage 手动设置图片(一般情况下是下载完成后自动设置)
if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
{
completedBlock(image, error, cacheType, url);
return;
}
else if (image) {
wself.image = image;
//刷新页面
[wself setNeedsLayout];
}else {
//如果下载失败,并且选择显示占位图的情况下,显示占位图
if ((options & SDWebImageDelayPlaceholder)) {
wself.image = placeholder;
[wself setNeedsLayout];
}
}
if (completedBlock && finished) {
completedBlock(image, error, cacheType, url);
}
});
}];
//进入下载图片
[self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];
} else {
//如果url为nil
dispatch_main_async_safe(^{
[self removeActivityIndicator];
if (completedBlock) {
NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
completedBlock(nil, error, SDImageCacheTypeNone, url);
}
});
}
}