前言
- 最近做挺多的圖片處理,
透視、縮放、拼接、裁剪、效果
等等,那麼今天就先來詳細對比一下系統API處理縮放的性能,這樣也好方便選擇那種更優的方式來處理
GitHub地址:KJExtensionHandler
大致分爲以下五種API:
- UIKit,畫布
drawInRect:
和UIGraphicsGetImageFromCurrentImageContext
- CoreGraphics / Quartz 2D,位圖上下文
CGContextScaleCTM
和CGContextDrawImage
- ImageIO,創建省略圖
CGImageSourceCreateWithData
和CGImageSourceCreateThumbnailAtIndex
- CoreImage,濾鏡
CILanczosScaleTransform
- Accelerate,
CGBitmapContextCreate
和CGContextDrawImage
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
綜合耗時比較
類型 | UIKit | CoreGraphics | ImageIO | CoreImage | Accelerate |
---|---|---|---|---|---|
PNG | 0.006449 | 0.005489 | 0.058294 | 0.139110 | 0.005719 |
JPG | 0.003134 | 0.001823 | 0.016345 | 0.134511 | 0.001897 |