UI相关音视频从入门到放弃ios developers

iOS 关于图片缩放性能探究

2020-11-24  本文已影响0人  弹吉他的少年

前言

PNG JPG

大致分为以下五种API:

  1. UIKit,画布 drawInRect:UIGraphicsGetImageFromCurrentImageContext
  2. CoreGraphics / Quartz 2D,位图上下文CGContextScaleCTMCGContextDrawImage
  3. ImageIO,创建省略图 CGImageSourceCreateWithDataCGImageSourceCreateThumbnailAtIndex
  4. CoreImage,滤镜 CILanczosScaleTransform
  5. Accelerate,vImage 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

绘图引擎,提供低级别、轻量级、高保真度的2D渲染

- (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的键值说明

- (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];
    CIContext *ciContext = [[CIContext alloc] initWithOptions:@{kCIContextUseSoftwareRenderer : @(NO)}];
    CGImageRef ciImageRef = [ciContext createCGImage:filter.outputImage fromRect:CGRectMake(0, 0, size.width, size.height)];
    UIImage *newImage = [UIImage imageWithCGImage:ciImageRef];
    return newImage;
}

Accelerate

vImage,使用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图片耗时对比,等比缩放

------- 🎈 给我点赞 🎈 -------
编译时间:14:11:38
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行号:38
打印信息:OriginalData:466290

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

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

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

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

------- 🎈 给我点赞 🎈 -------
编译时间:14:11:38
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行号:67
打印信息:AccelerateTime:0.008618,Data:35748

JPG耗时比较

------- 🎈 给我点赞 🎈 -------
编译时间:14:08:03
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行号:38
打印信息:OriginalData:189364

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

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

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

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

------- 🎈 给我点赞 🎈 -------
编译时间:14:08:03
文件名:KJImageCompressVC.m
方法名:-[KJImageCompressVC viewDidLoad]
行号:67
打印信息:AccelerateTime:0.001000,Data:19062

综合耗时比较

类型 UIKit CoreGraphics ImageIO CoreImage Accelerate
PNG 0.009362 0.009098 0.053086 0.128086 0.008618
JPG 0.001181 0.001097 0.010663 0.020129 0.001000
类型 原图 UIKit CoreGraphics ImageIO CoreImage Accelerate
PNG 466290 36902 36901 38576 40243 35748
JPG 189364 19778 19783 17099 21078 19062

总结

  1. Accelerate 压缩出来质量最小
  2. ImageIO 肉眼感觉清晰度最高
  3. ImageIO 和 CoreImage 只能做等比缩放

备注:本文用到的部分函数方法和Demo,均来自三方库KJCategories

图片缩放性能对比介绍就到此完毕,后面有相关再补充

上一篇 下一篇

猜你喜欢

热点阅读