【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
五、图片数据处理层
5.1 图片类型分析
图片类型分析是通过判断图片数据的第一个字节,有点类似MachO文件的MagicNumber。
使用十六进制打开图片可以验证
- jpg图像的十六进制,第一个字节为
0xFF
- png图像的十六进制,第一个字节为
0x89
,可以参考 RFC 文档中12.11. PNG file signature这一章节对PNG格式的介绍
对应的代码如下:
+ (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data {
if (!data) {
return SDImageFormatUndefined;
}
uint8_t c;
[data getBytes:&c length:1];
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;
}
5.2 GIF支持
这个版本的GIF支持是通过第三发的组件实现FLAnimatedImage
,主要代码是在FLAnimatedImageView+WebCache
分类的sd_setImageWithURL
方法中,主要代码如下,使用imageData
创建FLAnimatedImage
对象,设置到``FLAnimatedImageView的
image`属性中即可
- (void)sd_setImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock {
__weak typeof(self)weakSelf = self;
[self sd_internalSetImageWithURL:url
placeholderImage:placeholder
options:options
operationKey:nil
setImageBlock:^(UIImage *image, NSData *imageData) {
SDImageFormat imageFormat = [NSData sd_imageFormatForImageData:imageData];
if (imageFormat == SDImageFormatGIF) {
weakSelf.animatedImage = [FLAnimatedImage animatedImageWithGIFData:imageData];
weakSelf.image = nil;
} else {
weakSelf.image = image;
weakSelf.animatedImage = nil;
}
}
progress:progressBlock
completed:completedBlock];
}
5.3 Webp支持
Web解码可以查看这篇文章的介绍 Webp Decode ,其中最重要是解码器WebPDemuxer
。
- 创建解码器,其中
data
是NSData *
类型
WebPData webpData;
WebPDataInit(&webpData);
webpData.bytes = data.bytes;
webpData.size = data.length;
WebPDemuxer *demuxer = WebPDemux(&webpData);
- 获取图片信息,使用
WebPDemuxGetI
方法,可以获取图片宽高等信息,下面使用WEBP_FF_FORMAT_FLAGS
获取图片格式信息,判断是否是动态的webP,如果不是解码出单张图
uint32_t flags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS);
if (!(flags & ANIMATION_FLAG)) {
// for static single webp image
UIImage *staticImage = [self sd_rawWepImageWithData:webpData];
WebPDemuxDelete(demuxer);
return staticImage;
}
单张图的步骤如下 :
- 解码图像,图像的信息和解码的数据会保存在
WebPDecoderConfig
结构体中
WebPDecoderConfig config;
if (!WebPInitDecoderConfig(&config)) {
return nil;
}
if (WebPGetFeatures(webpData.bytes, webpData.size, &config.input) != VP8_STATUS_OK) {
return nil;
}
config.output.colorspace = config.input.has_alpha ? MODE_rgbA : MODE_RGB;
config.options.use_threads = 1;
// Decode the WebP image data into a RGBA value array.
if (WebPDecode(webpData.bytes, webpData.size, &config) != VP8_STATUS_OK) {
return nil;
}
int width = config.input.width;
int height = config.input.height;
if (config.options.use_scaling) {
width = config.options.scaled_width;
height = config.options.scaled_height;
}
- 创建图像,解码数据会保存在
config.output.u.RGBA
中,先创建CGDataProviderRef
对象provider
,然后再使用provider
创建CGImageRef
对象,重要的是CGImageCreate
这个方法,对应的参数做了注释提供参考
// Construct a UIImage from the decoded RGBA value array.
// 解码数据会保存在`config.output.u.RGBA`中,先创建`CGDataProviderRef`对象
CGDataProviderRef provider =
CGDataProviderCreateWithData(NULL, config.output.u.RGBA.rgba, config.output.u.RGBA.size, FreeImageData);
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = config.input.has_alpha ? kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast : 0;
size_t components = config.input.has_alpha ? 4 : 3;
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
// `provider`创建`CGImageRef`对象
// CGImageRef CGImageCreate(
// size_t width,// 图片宽度
// size_t height,// 图片高度
// size_t bitsPerComponent,// 图片颜色选项,文档中说明了32位真彩色(RGBA)的图像,该值需要传8
// size_t bitsPerPixel,// 图片单个像素点的位数,32位真彩色,带有透明通道(RGBA)的图像一个像素点为4字节32位,24位没有透明通道(RGB)的图像一个像素点为3字节24位
// size_t bytesPerRow,// 每一行的字节数,宽度*(4或者3)
// CGColorSpaceRef space,// RGB颜色空间,CGColorSpaceCreateDeviceRGB
// CGBitmapInfo bitmapInfo,// 位图的布局信息,带有alpha的使用kCGBitmapByteOrder32Big;没有alpha信息的使用kCGImageAlphaPremultipliedLast,可以参考 https://blog.csdn.net/jeffasd/article/details/78142067 https://blog.csdn.net/q375537943/article/details/59106787
// CGDataProviderRef provider, // 数据提供者
// const CGFloat *decode,
// bool shouldInterpolate, // 是否使用差值,如果没有插值,在分辨率高于图像数据的输出设备上绘制时,图像可能会出现锯齿或像素化。因为创建的图像没有放大,所以不用
// CGColorRenderingIntent intent // 这个值使用kCGRenderingIntentDefault即可,具体的详细资料没有找到
// );
CGImageRef imageRef = CGImageCreate(width, height, 8, components * 8, components * width, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
CGColorSpaceRelease(colorSpaceRef);
CGDataProviderRelease(provider);
UIImage *image = [[UIImage alloc] initWithCGImage:imageRef];
5.4 图片解码
解码的方法,主要的处理就是调用CGContextDrawImage
把图片进行绘制,然后使用CGBitmapContextCreateImage
获取绘制之后的图片,这个步骤是在异步队列中的,不会阻塞主线程。注意到这里使用了@autoreleasepool
是为了中间的临时图片数据能够被及时释放,这样能够有效的防止临时对象不能被及时释放导致高的内存峰值。
+ (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;
CGColorSpaceRef colorspaceRef = [UIImage colorSpaceForImageRef:imageRef];
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);
CGImageRef imageRefWithoutAlpha = CGBitmapContextCreateImage(context);
UIImage *imageWithoutAlpha = [UIImage imageWithCGImage:imageRefWithoutAlpha
scale:image.scale
orientation:image.imageOrientation];
CGContextRelease(context);
CGImageRelease(imageRefWithoutAlpha);
return imageWithoutAlpha;
}
}
来源:oschina
链接:https://my.oschina.net/FEEDFACF/blog/3143680