一、如何判断图片类型?
当文件通过二进制流数据进行传输时,需要制定规范,用来表明其数据类型。数据类型及其对应的文件头如下:
数据类型 | 文件头 |
---|---|
JPEG (jpg) | FFD8FFE1 |
PNG (png) | 89504E47 |
GIF (gif) | 47494638 |
TIFF tif;tiff | 0x4D4D002A |
RAR Archive (rar) | 52617221 |
WebP | 524946462A73010057454250 |
SDWebImage通过获取文件头的第一个字节的数据,然后比较其 ASIC 码,进而得出其数据类型。具体实现在 NSData+ImageContentType
中:
/**
根据图片NSData获取图片的类型
@param data NSData数据
@return 图片数据类型
*/
+ (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data {
if (!data) {
return SDImageFormatUndefined;
}
uint8_t c;
// 获取图片数据的第一个字节数据
[data getBytes:&c length:1];
// 根据字母的ASC码比较
switch (c) {
case 0xFF:
return SDImageFormatJPEG;
case 0x89:
return SDImageFormatPNG;
case 0x47:
return SDImageFormatGIF;
case 0x49:
case 0x4D:
return SDImageFormatTIFF;
case 0x52:
// R as RIFF for WEBP
if (data.length < 12) {
return SDImageFormatUndefined;
}
NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
return SDImageFormatWebP;
}
}
return SDImageFormatUndefined;
}
二、Core Foundation 对象的内存管理
底层的 Core Foundation 对象大对数以 XxxCreateWithXxx 方式创建,例如:
CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, "hello world", kCFStringEncodingUTF8);
对于这种类型对象的内存管理,要相应的使用 CFRetain 和 CFRealease 方法,我们可以直观的认为,这两种方法与 OC 对象的 retain 和 realease 方法等价。
在 ARC 下,Core Foundation 对象转化成 OC 对象时,原来的 Core Foundation 对象该怎样进行内存管理?
- __bridge只做类型转换,但是不修改对象(内存)管理权;
- __bridge_retained(也可以使用CFBridgingRetain)将Objective-C的对象转换为Core Foundation的对象,同时将对象(内存)的管理权交给我们,后续需要使用CFRelease或者相关方法来释放对象;
- __bridge_transfer(也可以使用CFBridgingRelease)将Core Foundation的对象转换为Objective-C的对象,同时将对象(内存)的管理权交给ARC。
三、如何获取 GIF 图片的第一张图片
UIImage+GIF
/**
根据gif图片的data生成对应的gif的UIImage对象。而且只会取GIF图片的第一张UIImage。
@param data gif图片的data对象
@return 生成的image对象。这里只获取gif图片的第一张图像,如果要实现gif完整图像,使用FLAnimatedImageView
*/
+ (UIImage *)sd_animatedGIFWithData:(NSData *)data {
if (!data) {
return nil;
}
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
// 获取GIF图片包含的UIImage数量
size_t count = CGImageSourceGetCount(source);
UIImage *staticImage;
// 如果只有一张UIImage
if (count <= 1) {
staticImage = [[UIImage alloc] initWithData:data];
} else {
// we will only retrieve the 1st frame. the full GIF support is available via the FLAnimatedImageView category.
// this here is only code to allow drawing animated images as static ones
CGFloat scale = 1;
scale = [UIScreen mainScreen].scale;
// 获取第一张UIImage对象
CGImageRef CGImage = CGImageSourceCreateImageAtIndex(source, 0, NULL);
// 获取gif图片的第一张图片
UIImage *frameImage = [UIImage imageWithCGImage:CGImage scale:scale orientation:UIImageOrientationUp];
// 用第一张图片生成一个新的gif图片
staticImage = [UIImage animatedImageWithImages:@[frameImage] duration:0.0f];
CGImageRelease(CGImage);
}
CFRelease(source);
return staticImage;
}
四、图片为什么需要解压缩?
- 不管是 JPEG 还是 PNG 图片,都是一种压缩的位图图形格式。只不过 PNG 图片是无损压缩,并且支持 alpha 通道,而 JPEG 图片则是有损压缩,可以指定 0-100% 的压缩比。
- 压缩的图形格式不能够被直接使用,必须解压成未压缩的位图格式。
- 位图就是一个像素数组,数组中的每个像素就代表着图片中的一个点
- 将磁盘中的图片渲染到屏幕之前,必须先要得到图片的原始像素数据,才能执行后续的绘制操作,这就是为什么需要对图片解压缩的原因。
当未解压缩的图片将要渲染到屏幕时,系统会在主线程对图片进行解压缩,而如果图片已经解压缩了,系统就不会再对图片进行解压缩。因此,也就有了业内的解决方案,在子线程提前对图片进行强制解压缩:
/**
解压缩图片
@param image UIImage对象
@return 返回解压缩以后的图片
*/
+ (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image {
//图片是否能够加压缩
if (![UIImage shouldDecodeImage:image]) {
return image;
}
// 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];
/*
*解压缩操作放入一个自动释放池里面。一遍自动释放所有的变量。
*/
@autoreleasepool{
CGImageRef imageRef = image.CGImage;
//获取图片的色彩空间
CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(CGImageGetColorSpace(imageRef));
CGColorSpaceRef colorspaceRef = CGImageGetColorSpace(imageRef);
BOOL unsupportedColorSpace = (imageColorSpaceModel == kCGColorSpaceModelUnknown ||
imageColorSpaceModel == kCGColorSpaceModelMonochrome ||
imageColorSpaceModel == kCGColorSpaceModelCMYK ||
imageColorSpaceModel == kCGColorSpaceModelIndexed);
if (unsupportedColorSpace) {
colorspaceRef = CGColorSpaceCreateDeviceRGB();
CFAutorelease(colorspaceRef);
}
//宽度和高度
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
//图片占用的字节数
size_t bytesPerRow = kBytesPerPixel * width;
// 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.
// 创建一个位图上下文
CGContextRef context = CGBitmapContextCreate(NULL,
width,
height,
kBitsPerComponent,
bytesPerRow,
colorspaceRef,
kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast);
if (context == NULL) {
return image;
}
// Draw the image into the context and retrieve the new bitmap image without alpha
// 将原始位图绘制到上下文中
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
// 创建一张新的解压缩后的没有alpha通道的位图
CGImageRef imageRefWithoutAlpha = CGBitmapContextCreateImage(context);
// 得到解压缩以后的图片
UIImage *imageWithoutAlpha = [UIImage imageWithCGImage:imageRefWithoutAlpha
scale:image.scale
orientation:image.imageOrientation];
CGContextRelease(context);
CGImageRelease(imageRefWithoutAlpha);
return imageWithoutAlpha;
}
}