iOS UIImage图片缩放性能对比

前言
  • 最近做挺多的图片处理,透视、缩放、拼接、裁剪、效果等等,那么今天就先来详细对比一下系统API处理缩放的性能,这样也好方便选择那种更优的方式来处理
GitHub地址:KJExtensionHandler

|

|
这里有一个iOS交流圈:891 488 181 不管你是大牛还是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!

大致分为以下五种API:
  1. UIKit,画布 drawInRect:UIGraphicsGetImageFromCurrentImageContext
  2. CoreGraphics / Quartz 2D,位图上下文CGContextScaleCTMCGContextDrawImage
  3. ImageIO,创建省略图 CGImageSourceCreateWithDataCGImageSourceCreateThumbnailAtIndex
  4. CoreImage,滤镜 CILanczosScaleTransform
  5. Accelerate,CGBitmapContextCreateCGContextDrawImage
API
/// UIKit方式
- (UIImage*)kj_UIKitChangeImageSize:(CGSize)size;
/// Quartz 2D
- (UIImage*)kj_QuartzChangeImageSize:(CGSize)size;
/// ImageIO
- (UIImage*)kj_ImageIOChangeImageSize:(CGSize)size;
/// CoreImage
- (UIImage*)kj_CoreImageChangeImageSize:(CGSize)size;
/// Accelerate
- (UIImage*)kj_AccelerateChangeImageSize:(CGSize)size;
UIKit

画布的形式,使用临时图形上下文来渲染缩放

- (UIImage*)kj_UIKitChangeImageSize:(CGSize)size{
    UIGraphicsBeginImageContext(size);
    [self drawInRect:CGRectMake(0, 0, size.width, size.height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}
CoreGraphics / Quartz 2D

这个其实和UIKit差不多

- (UIImage*)kj_QuartzChangeImageSize:(CGSize)size{
    UIGraphicsBeginImageContext(size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(context, 0.0, size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGContextSetBlendMode(context, kCGBlendModeCopy);
    CGContextDrawImage(context, CGRectMake(0, 0, size.width, size.height), self.CGImage);
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}
ImageIO

使用导入 #import <ImageIO/ImageIO.h> CGImageSource的键值说明

  • kCGImageSourceCreateThumbnailWithTransform - 设置缩略图是否进行Transfrom变换
  • kCGImageSourceCreateThumbnailFromImageAlways - 设置是否创建缩略图,无论原图像有没有包含缩略图,默认kCFBooleanFalse,影响 CGImageSourceCreateThumbnailAtIndex 方法
  • kCGImageSourceCreateThumbnailFromImageIfAbsent - 设置是否创建缩略图,如果原图像有没有包含缩略图,则创建缩略图,默认kCFBooleanFalse,影响 CGImageSourceCreateThumbnailAtIndex 方法
  • kCGImageSourceThumbnailMaxPixelSize - 设置缩略图的最大宽/高尺寸 需要设置为CFNumber值,设置后图片会根据最大宽/高 来等比例缩放图片
  • kCGImageSourceShouldCache - 设置是否以解码的方式读取图片数据 默认为kCFBooleanTrue,如果设置为true,在读取数据时就进行解码 如果为false 则在渲染时才进行解码
- (UIImage*)kj_ImageIOChangeImageSize:(CGSize)size{
    NSData *date = UIImagePNGRepresentation(self);
    CGFloat max = size.width;
    if (max < size.height) max = size.height;
    CFDictionaryRef dicOptionsRef = (__bridge CFDictionaryRef) @{(id)kCGImageSourceCreateThumbnailFromImageIfAbsent : @(YES),
                                                                 (id)kCGImageSourceThumbnailMaxPixelSize : @(max),
                                                                 (id)kCGImageSourceShouldCache : @(YES),};
    CGImageSourceRef src = CGImageSourceCreateWithData((__bridge CFDataRef)date, nil);
    CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(src, 0, dicOptionsRef);
    UIImage *newImage = [UIImage imageWithCGImage:imageRef];
    if (imageRef != nil) CFRelease(imageRef);
    CFRelease(src);
    return newImage;
}
CoreImage

滤镜的处理方式,综合对比下来,这种是最差 使用导入 #import <CoreImage/CoreImage.h>

- (UIImage*)kj_CoreImageChangeImageSize:(CGSize)size{
    CIImage *ciImage = [CIImage imageWithCGImage:self.CGImage];
    CGFloat scale = fminf(size.height/self.size.height, size.width/self.size.width);
    NSDictionary *dict = @{kCIInputScaleKey:@(scale),kCIInputAspectRatioKey:@(1.),kCIInputImageKey:ciImage};
    CIFilter *filter = [CIFilter filterWithName:@"CILanczosScaleTransform" withInputParameters:dict];
    UIImage *newImage = [UIImage imageWithCIImage:filter.outputImage];
    return newImage;
}
Accelerate

使用CPU的矢量处理器处理大图像 使用导入 #import <Accelerate/Accelerate.h>

- (UIImage*)kj_AccelerateChangeImageSize:(CGSize)size{
    const size_t width = size.width, height = size.height;
    const size_t bytesPerRow = width * 4;
    CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1
    int bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast;
#else
    int bitmapInfo = kCGImageAlphaPremultipliedLast;
#endif
    CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8, bytesPerRow, space, bitmapInfo);
    CGColorSpaceRelease(space);
    if (!bmContext) return nil;
    CGContextDrawImage(bmContext, CGRectMake(0, 0, width, height), self.CGImage);
    UInt8 * data = (UInt8*)CGBitmapContextGetData(bmContext);
    if (!data){
        CGContextRelease(bmContext);
        return nil;
    }
    CGImageRef imageRef = CGBitmapContextCreateImage(bmContext);
    UIImage *newImage = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);
    CGContextRelease(bmContext);
    return newImage;
}
测试示例
- (void)viewDidLoad {
    [super viewDidLoad];
    CGFloat x,y;
    CGFloat sp = kAutoW(10);
    CGFloat w = (kScreenW-sp*2)/2.;
    CGFloat h = (kScreenH-4*sp-kSTATUSBAR_NAVIGATION_HEIGHT)/3;
    NSArray *names = @[@"原图",@"UIKit",@"Quartz 2D",@"ImageIO",@"CoreImage",@"Accelerate"];
    UIImage *image = kGetImage(@"xxsf");
    CGSize size = CGSizeMake(image.size.width/4.3, image.size.height/4.3);
    for (int k=0; k<names.count; k++) {
        x = k%2*(w+sp)+sp/2;
        y = k/2*(h+sp)+sp+kSTATUSBAR_NAVIGATION_HEIGHT;
        UILabel *label = [UILabel kj_createLabelWithText:names[k] FontSize:16 TextColor:UIColor.orangeColor];
        label.frame = CGRectMake(x, y, w, 20);
        [self.view addSubview:label];
        UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(x, y+25, w, h-25)];
        imageView.contentMode = UIViewContentModeScaleAspectFit;
        imageView.backgroundColor = [UIColor.orangeColor colorWithAlphaComponent:0.1];
        [self.view addSubview:imageView];
        if (k==0) {
            imageView.image = image;
            NSData *date = UIImagePNGRepresentation(image);
            NSLog(@"OriginalData:%lu", (unsigned long)date.length);
        }else if (k==1) {
            CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
            UIImage *img = [image kj_UIKitChangeImageSize:size];
            NSData *date = UIImagePNGRepresentation(img);
            NSLog(@"UIKitTime:%f,Data:%lu", CFAbsoluteTimeGetCurrent() - start,(unsigned long)date.length);
            imageView.image = img;
        }else if (k==2) {
            CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
            UIImage *img = [image kj_QuartzChangeImageSize:size];
            NSData *date = UIImagePNGRepresentation(img);
            NSLog(@"QuartzTime:%f,Data:%lu", CFAbsoluteTimeGetCurrent() - start,(unsigned long)date.length);
            imageView.image = img;
        }else if (k==3) {
            CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
            UIImage *img = [image kj_ImageIOChangeImageSize:size];
            NSData *date = UIImagePNGRepresentation(img);
            NSLog(@"ImageIOTime:%f,Data:%lu", CFAbsoluteTimeGetCurrent() - start,(unsigned long)date.length);
            imageView.image = img;
        }else if (k==4) {
            CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
            UIImage *img = [image kj_CoreImageChangeImageSize:size];
            NSData *date = UIImagePNGRepresentation(img);
            NSLog(@"CoreImageTime:%f,Data:%lu", CFAbsoluteTimeGetCurrent() - start,(unsigned long)date.length);
            imageView.image = img;
        }else if (k==5) {
            CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
            UIImage *img = [image kj_AccelerateChangeImageSize:size];
            NSData *date = UIImagePNGRepresentation(img);
            NSLog(@"AccelerateTime:%f,Data:%lu", CFAbsoluteTimeGetCurrent() - start,(unsigned long)date.length);
            imageView.image = img;
        }
    }
}
png图片耗时对比,等比缩放
------- 🎈 给我点赞 🎈 -------
编译时间:15:29:08
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行号:38
打印信息:OriginalData:466290

------- 🎈 给我点赞 🎈 -------
编译时间:15:29:08
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行号:43
打印信息:UIKitTime:0.006449,Data:36902

------- 🎈 给我点赞 🎈 -------
编译时间:15:29:08
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行号:49
打印信息:QuartzTime:0.005489,Data:36901

------- 🎈 给我点赞 🎈 -------
编译时间:15:29:08
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行号:55
打印信息:ImageIOTime:0.058294,Data:38576

------- 🎈 给我点赞 🎈 -------
编译时间:15:29:08
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行号:61
打印信息:CoreImageTime:0.139110,Data:140840

------- 🎈 给我点赞 🎈 -------
编译时间:15:29:08
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行号:67
打印信息:AccelerateTime:0.005719,Data:35748
复制代码
JPG耗时比较
------- 🎈 给我点赞 🎈 -------
编译时间:15:38:06
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行号:38
打印信息:OriginalData:183457

------- 🎈 给我点赞 🎈 -------
编译时间:15:38:06
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行号:43
打印信息:UIKitTime:0.003134,Data:19614

------- 🎈 给我点赞 🎈 -------
编译时间:15:38:06
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行号:49
打印信息:QuartzTime:0.001823,Data:19632

------- 🎈 给我点赞 🎈 -------
编译时间:15:38:06
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行号:55
打印信息:ImageIOTime:0.016345,Data:17158

------- 🎈 给我点赞 🎈 -------
编译时间:15:38:06
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行号:61
打印信息:CoreImageTime:0.134511,Data:72229

------- 🎈 给我点赞 🎈 -------
编译时间:15:38:06
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行号:67
打印信息:AccelerateTime:0.001897,Data:18922
综合耗时比较
类型UIKitCoreGraphicsImageIOCoreImageAccelerate
PNG0.0064490.0054890.0582940.1391100.005719
JPG0.0031340.0018230.0163450.1345110.001897
-------------------------------------------------------
###### Accelerate 压缩出来质量最小
###### ImageIO 肉眼感觉清晰度最高
###### ImageIO 和 CoreImage 只能做等比缩放
-------------------------------------------------------
感兴趣的朋友可以换不同尺寸的图片多次测试,这样就可得到每种方式在不同区域的性能对比

这里有一个iOS交流圈:891 488 181 不管你是大牛还是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!

备注:本文用到的部分函数方法和Demo,均来自三方库**KJExtensionHandler**,如有需要的朋友可自行pod 'KJExtensionHandler'引入即可

图片缩放性能对比介绍就到此完毕

作者:茶底世界之下
链接:https://juejin.cn/post/6898505496295112711

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值