Permalink
Fetching contributors…
Cannot retrieve contributors at this time
743 lines (639 sloc) 33.5 KB
//
// YYImageProfileExample.m
// YYKitExample
//
// Created by ibireme on 15/8/10.
// Copyright (c) 2015 ibireme. All rights reserved.
//
#import "YYImageBenchmark.h"
#import <YYKit/YYKit.h>
#import <ImageIO/ImageIO.h>
#import <MobileCoreServices/MobileCoreServices.h>
#import "YYBPGCoder.h"
/*
Enable this value and run in simulator, the image will write to desktop.
Then you can view this image with preview.
*/
#define ENABLE_OUTPUT 0
#define IMAGE_OUTPUT_DIR @"/Users/ibireme/Desktop/image_out/"
@implementation YYImageBenchmark {
UIActivityIndicatorView *_indicator;
UIView *_hud;
NSMutableArray *_titles;
NSMutableArray *_blocks;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self initHUD];
_titles = [NSMutableArray new];
_blocks = [NSMutableArray new];
self.title = @"Benchmark (See Logs in Xcode)";
[self addCell:@"ImageIO Image Decode" selector:@selector(runImageDecodeBenchmark)];
[self addCell:@"ImageIO Image Encode" selector:@selector(runImageEncodeBenchmark)];
[self addCell:@"WebP Encode and Decode (Slow)" selector:@selector(runWebPBenchmark)];
[self addCell:@"BPG Decode" selector:@selector(runBPGBenchmark)];
[self addCell:@"Animated Image Decode" selector:@selector(runAnimatedImageBenchmark)];
[self.tableView reloadData];
}
- (void)addCell:(NSString *)title selector:(SEL)sel {
__weak typeof(self) _self = self;
void (^block)(void) = ^() {
if (![_self respondsToSelector:sel]) return;
[_self startHUD];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[_self performSelector:sel];
#pragma clang diagnostic pop
dispatch_async(dispatch_get_main_queue(), ^{
[_self stopHUD];
});
});
};
[_titles addObject:title];
[_blocks addObject:block];
}
- (void)dealloc {
[_hud removeFromSuperview];
}
- (void)initHUD {
_hud = [UIView new];
_hud.size = CGSizeMake(130, 80);
_hud.backgroundColor = [UIColor colorWithWhite:0.000 alpha:0.7];
_hud.clipsToBounds = YES;
_hud.layer.cornerRadius = 5;
_indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
_indicator.size = CGSizeMake(50, 50);
_indicator.centerX = _hud.width / 2;
_indicator.centerY = _hud.height / 2 - 9;
[_hud addSubview:_indicator];
UILabel *label = [UILabel new];
label.textAlignment = NSTextAlignmentCenter;
label.size = CGSizeMake(_hud.width, 20);
label.text = @"See logs in Xcode";
label.font = [UIFont systemFontOfSize:12];
label.textColor = [UIColor whiteColor];
label.centerX = _hud.width / 2;
label.bottom = _hud.height - 8;
[_hud addSubview:label];
}
- (void)startHUD {
UIWindow *window = [[UIApplication sharedApplication].windows firstObject];
_hud.center = CGPointMake(window.width / 2, window.height / 2);
[_indicator startAnimating];
[window addSubview:_hud];
self.navigationController.view.userInteractionEnabled = NO;
}
- (void)stopHUD {
[_indicator stopAnimating];
[_hud removeFromSuperview];
self.navigationController.view.userInteractionEnabled = YES;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
((void (^)(void))_blocks[indexPath.row])();
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _titles.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"YY"];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"YY"];
}
cell.textLabel.text = _titles[indexPath.row];
return cell;
}
#pragma mark - Benchmark
- (NSArray *)imageNames {
return @[ @"dribbble", @"lena" ];
}
- (NSArray *)imageSizes {
return @[ @64, @128, @256, @512 ];
}
- (NSArray *)imageSources {
return @[ @"imageio", @"photoshop", @"imageoptim", @"pngcrush", @"tinypng", @"twitter", @"weibo", @"facebook" ];
}
- (NSArray *)imageTypes {
return @[ (id)kUTTypeJPEG, (id)kUTTypeJPEG2000, (id)kUTTypeTIFF, (id)kUTTypeGIF, (id)kUTTypePNG, (id)kUTTypeBMP ];
}
- (NSString *)imageTypeGetExt:(id)type {
static NSDictionary *map;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
map = @{(id)kUTTypeJPEG : @"jpg",
(id)kUTTypeJPEG2000 : @"jp2",
(id)kUTTypeTIFF : @"tif",
(id)kUTTypeGIF : @"gif",
(id)kUTTypePNG : @"png",
(id)kUTTypeBMP : @"bmp"};
});
return type ? map[type] : nil;
}
- (NSArray *)imageTypeGetQuality:(NSString *)type {
BOOL hasQuality = [type isEqualToString:(id)kUTTypeJPEG] || [type isEqualToString:(id)kUTTypeJPEG2000] || [type isEqualToString:@"webp"];
return hasQuality ? @[@1.0, @0.95, @0.9, @0.85, @0.8, @0.75, @0.7, @0.6, @0.5, @0.4, @0.3, @0.2, @0.1, @0] : @[@1.0];
}
- (void)runImageDecodeBenchmark {
printf("==========================================\n");
printf("ImageIO Decode Benchmark\n");
printf("name size type quality length decode_time\n");
for (NSString *imageName in self.imageNames) {
for (NSNumber *imageSize in self.imageSizes) {
for (NSString *imageSource in self.imageSources) {
for (NSString *imageType in @[@"png", @"jpg"]) {
@autoreleasepool {
NSString *fileName = [NSString stringWithFormat:@"%@%@_%@",imageName, imageSize, imageSource];
NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:imageType];
NSData *data = filePath ? [NSData dataWithContentsOfFile:filePath] : nil;
if (!data) continue;
int count = 100;
YYBenchmark(^{
for (int i = 0; i < count; i++) {
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFTypeRef)data, NULL);
CGImageRef image = CGImageSourceCreateImageAtIndex(source, 0, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(NO)});
CGImageRef decoded = YYCGImageCreateDecodedCopy(image, YES);
CFRelease(decoded);
CFRelease(image);
CFRelease(source);
}
}, ^(double ms) {
printf("%8s %3d %3s %10s %6d %2.3f\n", imageName.UTF8String, imageSize.intValue, imageType.UTF8String, imageSource.UTF8String, (int)data.length, ms / count);
});
#if ENABLE_OUTPUT
if ([UIDevice currentDevice].isSimulator) {
NSString *outFilePath = [NSString stringWithFormat:@"%@%@.%@", IMAGE_OUTPUT_DIR, fileName, imageType];
[data writeToFile:outFilePath atomically:YES];
}
#endif
}
}
}
}
}
printf("------------------------------------------\n\n");
}
- (void)runImageEncodeBenchmark {
printf("==========================================\n");
printf("ImageIO Encode Benchmark\n");
printf("name size type quality length encode decode\n");
for (NSString *imageName in self.imageNames) {
for (NSNumber *imageSize in self.imageSizes) {
NSString *fileName = [NSString stringWithFormat:@"%@%@_imageio",imageName, imageSize];
NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@"png"];
NSData *data = filePath ? [NSData dataWithContentsOfFile:filePath] : nil;
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFTypeRef)data, NULL);
CGImageRef image = CGImageSourceCreateImageAtIndex(source, 0, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(NO)});
CGImageRef decoded = YYCGImageCreateDecodedCopy(image, YES);
for (NSString *uti in [self imageTypes]) {
for (NSNumber *quality in [self imageTypeGetQuality:uti]) {
__block int encodeCount = 0;
__block double encodeTime = 0;
__block long length = 0;
__block CFMutableDataRef outData = NULL;
__block int decodeCount = 0;
__block double decodeTime = 0;
while (encodeTime < 200) { //200ms
YYBenchmark(^{
if (outData) CFRelease(outData);
outData = CFDataCreateMutable(CFAllocatorGetDefault(), 0);
CGImageDestinationRef dest = CGImageDestinationCreateWithData(outData, (CFStringRef)uti, 1, NULL);
NSDictionary *options = @{(id)kCGImageDestinationLossyCompressionQuality : quality };
CGImageDestinationAddImage(dest, decoded, (CFDictionaryRef)options);
CGImageDestinationFinalize(dest);
length = CFDataGetLength(outData);
CFRelease(dest);
}, ^(double ms) {
encodeTime += ms;
encodeCount += 1;
});
}
#if ENABLE_OUTPUT
if ([UIDevice currentDevice].isSimulator) {
NSString *outFilePath = [NSString stringWithFormat:@"%@%@%@_%.2f.%@", IMAGE_OUTPUT_DIR, imageName, imageSize, quality.floatValue, [self imageTypeGetExt:uti]];
[((__bridge NSData *)outData) writeToFile:outFilePath atomically:YES];
}
#endif
decodeCount = 100;
YYBenchmark(^{
for (int i = 0; i < decodeCount; i++) {
CGImageSourceRef source = CGImageSourceCreateWithData(outData, NULL);
CGImageRef image = CGImageSourceCreateImageAtIndex(source, 0, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(NO)});
CGImageRef decoded = YYCGImageCreateDecodedCopy(image, NO);
CFRelease(decoded);
CFRelease(image);
CFRelease(source);
}
}, ^(double ms) {
decodeTime = ms;
});
CFRelease(outData);
printf("%8s %3d %3s %.2f %7d %7.3f %7.3f\n",imageName.UTF8String, imageSize.intValue, [self imageTypeGetExt:uti].UTF8String, quality.floatValue, (int)length, encodeTime / encodeCount, decodeTime / decodeCount);
}
}
CFRelease(decoded);
CFRelease(image);
CFRelease(source);
}
}
printf("------------------------------------------\n\n");
}
- (void)runWebPBenchmark {
printf("==========================================\n");
printf("WebP Benchmark\n");
printf("name size type quality method length encode decode\n");
for (NSString *imageName in self.imageNames) {
for (NSNumber *imageSize in self.imageSizes) {
NSString *fileName = [NSString stringWithFormat:@"%@%@_imageio", imageName, imageSize];
NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@"png"];
NSData *data = filePath ? [NSData dataWithContentsOfFile:filePath] : nil;
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFTypeRef)data, NULL);
CGImageRef image = CGImageSourceCreateImageAtIndex(source, 0, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache : @(NO) });
CGImageRef decoded = YYCGImageCreateDecodedCopy(image, YES);
for (NSNumber *lossless in @[ @YES, @NO ]) {
for (NSNumber *q in [self imageTypeGetQuality:@"webp"]) {
for (NSNumber *m in @[ @0, @1, @2, @3, @4, @5, @6 ]) {
@autoreleasepool {
__block int encodeCount = 0;
__block double encodeTime = 0;
__block long length = 0;
__block CFDataRef webpData = NULL;
int decodeCount = 100;
double decodeTime[8] = {0}; // useThreads,bypassFiltering,noFancyUpsampling 0,0,0; 0,0,1; 0,1,0; 0,1,1; 1,0,0; 1,0,1; 1,1,0; 1,1,1
while (encodeTime < 200) { // 200ms
YYBenchmark( ^{
if (webpData) CFRelease(webpData);
webpData = YYCGImageCreateEncodedWebPData(decoded, lossless.boolValue, q.floatValue, m.intValue, YYImagePresetDefault);
length = CFDataGetLength(webpData);
}, ^(double ms) {
encodeTime += ms;
encodeCount += 1;
});
}
#if ENABLE_OUTPUT
if ([UIDevice currentDevice].isSimulator) {
NSString *outFilePath = [NSString
stringWithFormat:@"%@%@%@_%@_q%.2f_m%d.webp", IMAGE_OUTPUT_DIR, imageName, imageSize,
lossless.boolValue ? @"lossless" : @"lossy", q.floatValue, m.intValue];
[((__bridge NSData *)webpData)writeToFile:outFilePath atomically:YES];
CGImageRef image = YYCGImageCreateWithWebPData(webpData, NO, NO, NO, NO);
NSData *pngData = UIImagePNGRepresentation([UIImage imageWithCGImage:image]);
NSString *pngOutFilePath = [NSString
stringWithFormat:@"%@%@%@_%@_q%.2f_m%d.webp.png", IMAGE_OUTPUT_DIR, imageName, imageSize,
lossless.boolValue ? @"lossless" : @"lossy", q.floatValue, m.intValue];
[pngData writeToFile:pngOutFilePath atomically:YES];
CFRelease(image);
}
#endif
for (NSNumber *useThreads in @[ @NO, @YES ]) {
for (NSNumber *bypassFiltering in @[ @NO, @YES ]) {
for (NSNumber *noFancyUpsampling in @[ @NO, @YES ]) {
__block double time = 0;
YYBenchmark(^{
for (int i = 0; i < decodeCount; i++) {
CGImageRef image = YYCGImageCreateWithWebPData(webpData, YES, useThreads.boolValue, bypassFiltering.boolValue,noFancyUpsampling.boolValue);
CFRelease(image);
}
}, ^(double ms) {
time = ms;
});
decodeTime[useThreads.intValue << 2 | bypassFiltering.intValue << 1 |
noFancyUpsampling.intValue] = time;
}
}
}
if (webpData) CFRelease(webpData);
printf("%8s %3d %.8s %.2f %1d %7d %9.3f %7.3f %7.3f %7.3f %7.3f %7.3f %7.3f %7.3f %7.3f\n",
imageName.UTF8String, imageSize.intValue, lossless.boolValue ? "lossless" : "lossy",
q.floatValue, m.intValue, (int)length, encodeTime / encodeCount, decodeTime[0] / decodeCount,
decodeTime[1] / decodeCount, decodeTime[2] / decodeCount, decodeTime[3] / decodeCount,
decodeTime[4] / decodeCount, decodeTime[5] / decodeCount, decodeTime[6] / decodeCount,
decodeTime[7] / decodeCount);
}
}
}
}
CFRelease(decoded);
CFRelease(image);
CFRelease(source);
}
}
printf("------------------------------------------\n\n");
}
- (void)runBPGBenchmark {
printf("==========================================\n");
printf("BPG Decode Benchmark\n");
printf("name size quality length decode_time\n");
for (NSString *imageName in self.imageNames) {
for (NSNumber *imageSize in self.imageSizes) {
for (NSString *quality in @[ @"lossless",@"q0",@"q5",@"q10",@"q15",@"q20",@"q25",@"q30",@"q35",@"q40",@"q45",@"q50"]) {
@autoreleasepool {
NSString *fileName = [NSString stringWithFormat:@"%@%@_%@",imageName, imageSize, quality];
NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@"bpg"];
NSData *data = filePath ? [NSData dataWithContentsOfFile:filePath] : nil;
if (!data) continue;
int count = 100;
YYBenchmark(^{
for (int i = 0; i < count; i++) {
CGImageRef image = YYCGImageCreateWithBPGData((__bridge CFDataRef)data, YES);
CFRelease(image);
}
}, ^(double ms) {
printf("%8s %3d %8s %6d %2.3f\n", imageName.UTF8String, imageSize.intValue, quality.UTF8String, (int)data.length, ms / count);
});
#if ENABLE_OUTPUT
if ([UIDevice currentDevice].isSimulator) {
NSString *outFilePath = [NSString stringWithFormat:@"%@%@.bpg", IMAGE_OUTPUT_DIR,fileName];
[data writeToFile:outFilePath atomically:YES];
CGImageRef image = YYCGImageCreateWithBPGData((__bridge CFDataRef)data, YES);
NSData *pngData = UIImagePNGRepresentation([UIImage imageWithCGImage:image]);
CFRelease(image);
NSString *pngOutFilePath = [NSString stringWithFormat:@"%@%@.bpg.png", IMAGE_OUTPUT_DIR,fileName];
[pngData writeToFile:pngOutFilePath atomically:YES];
}
#endif
}
}
}
}
printf("------------------------------------------\n\n");
}
- (void)runAnimatedImageBenchmark {
printf("==========================================\n");
printf("Animated Image Decode Benckmark\n");
if (!kiOS8Later) {
printf("APNG require iOS8 or later\n");
return;
}
NSData *gif = [NSData dataNamed:@"ermilio.gif"];
NSData *apng = [NSData dataNamed:@"ermilio.png"];
NSData *webp_q85 = [NSData dataNamed:@"ermilio_q85.webp"];
NSData *webp_q90 = [NSData dataNamed:@"ermilio_q90.webp"];
NSData *webp_lossless = [NSData dataNamed:@"ermilio_lossless.webp"];
NSData *bpg_q15 = [NSData dataNamed:@"ermilio_q15.bpg"];
NSData *bpg_q20 = [NSData dataNamed:@"ermilio_q20.bpg"];
NSData *bpg_lossless = [NSData dataNamed:@"ermilio_lossless.bpg"];
NSArray *datas = @[gif, apng, webp_q85, webp_q90, webp_lossless, bpg_q20, bpg_q15, bpg_lossless];
NSArray *names = @[@"gif", @"apng", @"webp_85", @"webp_90", @"webp_ll", @"bpg_20", @"bpg_15", @"bpg_ll"];
#if ENABLE_OUTPUT
if ([UIDevice currentDevice].isSimulator) {
@autoreleasepool {
NSString *outPath = [NSString stringWithFormat:@"%@ermilio.gif.png",IMAGE_OUTPUT_DIR];
NSData *outData = UIImagePNGRepresentation([UIImage imageWithData:gif]);
[outData writeToFile:outPath atomically:YES];
[gif writeToFile:[NSString stringWithFormat:@"%@ermilio.gif",IMAGE_OUTPUT_DIR] atomically:YES];
}
@autoreleasepool {
NSString *outPath = [NSString stringWithFormat:@"%@ermilio.apng.png",IMAGE_OUTPUT_DIR];
NSData *outData = UIImagePNGRepresentation([UIImage imageWithData:apng]);
[outData writeToFile:outPath atomically:YES];
[apng writeToFile:[NSString stringWithFormat:@"%@ermilio.png",IMAGE_OUTPUT_DIR] atomically:YES];
}
@autoreleasepool {
NSString *outPath = [NSString stringWithFormat:@"%@ermilio_q85.webp.png",IMAGE_OUTPUT_DIR];
NSData *outData = UIImagePNGRepresentation([YYImageDecoder decodeImage:webp_q85 scale:1]);
[outData writeToFile:outPath atomically:YES];
[webp_q85 writeToFile:[NSString stringWithFormat:@"%@ermilio_q85.webp",IMAGE_OUTPUT_DIR] atomically:YES];
}
@autoreleasepool {
NSString *outPath = [NSString stringWithFormat:@"%@ermilio_q90.webp.png",IMAGE_OUTPUT_DIR];
NSData *outData = UIImagePNGRepresentation([YYImageDecoder decodeImage:webp_q90 scale:1]);
[outData writeToFile:outPath atomically:YES];
[webp_q90 writeToFile:[NSString stringWithFormat:@"%@ermilio_q90.webp",IMAGE_OUTPUT_DIR] atomically:YES];
}
@autoreleasepool {
NSString *outPath = [NSString stringWithFormat:@"%@ermilio_lossless.webp.png",IMAGE_OUTPUT_DIR];
NSData *outData = UIImagePNGRepresentation([YYImageDecoder decodeImage:webp_lossless scale:1]);
[outData writeToFile:outPath atomically:YES];
[webp_lossless writeToFile:[NSString stringWithFormat:@"%@ermilio_lossless.webp",IMAGE_OUTPUT_DIR] atomically:YES];
}
@autoreleasepool {
NSString *outPath = [NSString stringWithFormat:@"%@ermilio_q15.bpg.png",IMAGE_OUTPUT_DIR];
CGImageRef imageRef = YYCGImageCreateWithBPGData((__bridge CFDataRef)bpg_q15, NO);
NSData *outData = UIImagePNGRepresentation([UIImage imageWithCGImage:imageRef]);
[outData writeToFile:outPath atomically:YES];
CFRelease(imageRef);
[bpg_q15 writeToFile:[NSString stringWithFormat:@"%@ermilio_q15.bpg",IMAGE_OUTPUT_DIR] atomically:YES];
}
@autoreleasepool {
NSString *outPath = [NSString stringWithFormat:@"%@ermilio_q20.bpg.png",IMAGE_OUTPUT_DIR];
CGImageRef imageRef = YYCGImageCreateWithBPGData((__bridge CFDataRef)bpg_q20, NO);
NSData *outData = UIImagePNGRepresentation([UIImage imageWithCGImage:imageRef]);
[outData writeToFile:outPath atomically:YES];
CFRelease(imageRef);
[bpg_q20 writeToFile:[NSString stringWithFormat:@"%@ermilio_q20.bpg",IMAGE_OUTPUT_DIR] atomically:YES];
}
@autoreleasepool {
NSString *outPath = [NSString stringWithFormat:@"%@ermilio_lossless.bpg.png",IMAGE_OUTPUT_DIR];
CGImageRef imageRef = YYCGImageCreateWithBPGData((__bridge CFDataRef)bpg_lossless, NO);
NSData *outData = UIImagePNGRepresentation([UIImage imageWithCGImage:imageRef]);
[outData writeToFile:outPath atomically:YES];
CFRelease(imageRef);
[bpg_lossless writeToFile:[NSString stringWithFormat:@"%@ermilio_lossless.bpg",IMAGE_OUTPUT_DIR] atomically:YES];
}
}
#endif
printf("------------------------------------------\n");
printf("image length\n");
for (int i = 0; i < names.count; i++) {
NSString *name = names[i];
NSData *data = datas[i];
printf("%7s %6d\n",name.UTF8String, (int)data.length);
}
printf("\n\n");
int count = 20;
int frame_num = 28;
typedef void (^CoverDecodeBlock)(id src);
typedef void (^SingleFrameDecodeBlock)(id src, NSUInteger index);
typedef void (^AllFrameDecodeBlock)(id src, BOOL reverseOrder);
/// Cover: gif/apng
CoverDecodeBlock imageioCoverDecoder = ^(NSData *data){
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFTypeRef)data, NULL);
CGImageRef image = CGImageSourceCreateImageAtIndex(source, 0, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(NO)});
CGImageRef decoded = YYCGImageCreateDecodedCopy(image, YES);
CFRelease(decoded);
CFRelease(image);
CFRelease(source);
};
/// Cover: gif/apng/webp
CoverDecodeBlock yyCoverDecoder = ^(NSData *data) {
@autoreleasepool {
YYImageDecoder *decoder = [YYImageDecoder decoderWithData:data scale:1];
[decoder frameAtIndex:0 decodeForDisplay:YES];;
}
};
/// Cover: webp
CoverDecodeBlock webpCoverDecoder = ^(NSData *data) {
CGImageRef image = YYCGImageCreateWithWebPData((__bridge CFDataRef)data, YES, NO, NO, NO);
CFRelease(image);
};
/// Cover: bpg
CoverDecodeBlock bpgCoverDecoder = ^(NSData *data) {
CGImageRef image = YYCGImageCreateWithBPGData((__bridge CFDataRef)data, YES);
CFRelease(image);
};
NSArray *coverSrcs = @[@"gif imageio", gif, imageioCoverDecoder,
@"gif yydecoder", gif, yyCoverDecoder,
@"apng imageio", apng, imageioCoverDecoder,
@"apng yydecoder", apng, yyCoverDecoder,
@"webp_85 yyimage", webp_q85, webpCoverDecoder,
@"webp_85 yydecocer", webp_q85, yyCoverDecoder,
@"webp_90 yyimage", webp_q90, webpCoverDecoder,
@"webp_90 yydecocer", webp_q90, yyCoverDecoder,
@"webp_ll yyimage", webp_lossless, webpCoverDecoder,
@"webp_ll yydecoder", webp_lossless, yyCoverDecoder,
@"bpg_20 yyimage", bpg_q20, bpgCoverDecoder,
@"bpg_15 yyimage", bpg_q20, bpgCoverDecoder,
@"bpg_ll yyimage", bpg_lossless, bpgCoverDecoder,
];
printf("------------------------------------------\n");
printf("First frame (cover) decode\n");
count = 20;
for (int i = 0; i < coverSrcs.count / 3; i++) {
NSString *name = coverSrcs[i * 3];
id src = coverSrcs[i * 3 + 1];
CoverDecodeBlock block = coverSrcs[i * 3 + 2];
YYBenchmark(^{
for (int r = 0; r < count; r++) {
block(src);
}
}, ^(double ms) {
printf("%s %8.3f\n",name.UTF8String, ms / count);
});
}
printf("\n\n");
/// Single: gif/apng
SingleFrameDecodeBlock imagioSingleFrameDecoder = ^(id src, NSUInteger index) {
CGImageSourceRef source = (__bridge CGImageSourceRef)src;
CGImageRef image = CGImageSourceCreateImageAtIndex(source, index, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(NO)});
CGImageRef decoded = YYCGImageCreateDecodedCopy(image, YES);
CFRelease(decoded);
CFRelease(image);
};
/// Single: gif/apng/webp
SingleFrameDecodeBlock yySingleFrameDecoder = ^(YYImageDecoder *decoder, NSUInteger index) {
@autoreleasepool {
UIImage *img = [decoder frameAtIndex:index decodeForDisplay:YES].image;
[img scale];
}
};
NSArray *singleSrcs = @[@"gif imageio", @"gif src", imagioSingleFrameDecoder,
@"gif yydecoder", @"gif", yySingleFrameDecoder,
@"apng imageio", @"apng src", imagioSingleFrameDecoder,
@"apng yydecoder", @"apng", yySingleFrameDecoder,
@"webp_85 yydecocer", @"webp85", yySingleFrameDecoder,
@"webp_90 yydecocer", @"webp90", yySingleFrameDecoder,
@"webp_ll yydecoder", @"webpll", yySingleFrameDecoder,
];
printf("------------------------------------------\n");
printf("Single frame decode\n");
count = 5;
for (int i = 0; i < singleSrcs.count / 3; i++) {
NSString *name = singleSrcs[i * 3];
NSString *srcStr = singleSrcs[i * 3 + 1];
SingleFrameDecodeBlock block = singleSrcs[i * 3 + 2];
printf("%s ",name.UTF8String);
for (int f = 0; f < frame_num; f++) {
YYBenchmark(^{
for (int r = 0; r < count; r++) {
id src = NULL;
if ([srcStr isEqual:@"gif src"]) {
src = CFBridgingRelease(CGImageSourceCreateWithData((__bridge CFDataRef)gif, NULL));
} else if ([srcStr isEqual:@"gif"]) {
src = [YYImageDecoder decoderWithData:gif scale:1];
} else if ([srcStr isEqual:@"apng src"]) {
src = CFBridgingRelease(CGImageSourceCreateWithData((__bridge CFDataRef)apng, NULL));
} else if ([srcStr isEqual:@"apng"]) {
src = [YYImageDecoder decoderWithData:apng scale:1];
} else if ([srcStr isEqual:@"webp85"]) {
src = [YYImageDecoder decoderWithData:webp_q85 scale:1];
} else if ([srcStr isEqual:@"webp90"]) {
src = [YYImageDecoder decoderWithData:webp_q90 scale:1];
} else if ([srcStr isEqual:@"webpll"]) {
src = [YYImageDecoder decoderWithData:webp_lossless scale:1];
}
block(src, f);
}
}, ^(double ms) {
printf("%8.3f ",ms / count);
});
}
printf("\n");
}
printf("\n\n");
/// All: gif/apng
AllFrameDecodeBlock imageioAllFrameDecoder = ^(NSData *data, BOOL reverseOrder){
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFTypeRef)gif, NULL);
if (reverseOrder) {
for (int i = frame_num - 1; i >= 0; i--) {
CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(NO)});
CGImageRef decoded = YYCGImageCreateDecodedCopy(image, YES);
CFRelease(decoded);
CFRelease(image);
}
} else {
for (int i = 0; i < frame_num; i++) {
CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(NO)});
CGImageRef decoded = YYCGImageCreateDecodedCopy(image, YES);
CFRelease(decoded);
CFRelease(image);
}
}
CFRelease(source);
};
/// All: gif/apng/webp
AllFrameDecodeBlock yyAllFrameDecoder = ^(NSData *data, BOOL reverseOrder){
@autoreleasepool {
YYImageDecoder *decoder = [YYImageDecoder decoderWithData:data scale:1];
if (reverseOrder) {
for (int i = frame_num - 1; i > 0; i--) {
[decoder frameAtIndex:i decodeForDisplay:YES];
}
} else {
for (int i = 1; i < frame_num; i++) {
[decoder frameAtIndex:i decodeForDisplay:YES];
}
}
}
};
/// All: bpg
AllFrameDecodeBlock bpgAllFrameDecoder = ^(NSData *data, BOOL reverseOrder){
@autoreleasepool {
YYCGImageDecodeAllFrameInBPGData((__bridge CFDataRef)data, YES);
}
};
NSArray *allSrcs = @[@"gif imageio", gif, imageioAllFrameDecoder,
@"gif yydecoder", gif, yyAllFrameDecoder,
@"apng imageio", apng, imageioAllFrameDecoder,
@"apng yydecoder", apng, yyAllFrameDecoder,
@"webp_85 yydecocer", webp_q85, yyAllFrameDecoder,
@"webp_90 yydecocer", webp_q90, yyAllFrameDecoder,
@"webp_ll yydecoder", webp_lossless, yyAllFrameDecoder,
@"bpg_20 yyimage", bpg_q20, bpgAllFrameDecoder,
@"bpg_15 yyimage", bpg_q20, bpgAllFrameDecoder,
@"bpg_ll yyimage", bpg_lossless, bpgAllFrameDecoder,
];
printf("------------------------------------------\n");
printf("All frame decode\n");
printf("type decoder asc desc\n");
count = 5;
for (int i = 0; i < allSrcs.count / 3; i++) {
NSString *name = allSrcs[i * 3];
id src = allSrcs[i * 3 + 1];
AllFrameDecodeBlock block = allSrcs[i * 3 + 2];
printf("%s ",name.UTF8String);
for (NSNumber *rev in @[@NO, @YES]) {
if ([name hasPrefix:@"bpg"] && rev.boolValue) continue;
YYBenchmark(^{
for (int r = 0; r < count; r++) {
block(src, rev.boolValue);
}
}, ^(double ms) {
printf("%8.3f ",ms / count);
});
}
printf("\n");
}
printf("\n\n");
}
@end