YYImageBenchmark.m 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742
  1. //
  2. // YYImageProfileExample.m
  3. // YYKitExample
  4. //
  5. // Created by ibireme on 15/8/10.
  6. // Copyright (c) 2015 ibireme. All rights reserved.
  7. //
  8. #import "YYImageBenchmark.h"
  9. #import <YYKit/YYKit.h>
  10. #import <ImageIO/ImageIO.h>
  11. #import <MobileCoreServices/MobileCoreServices.h>
  12. #import "YYBPGCoder.h"
  13. /*
  14. Enable this value and run in simulator, the image will write to desktop.
  15. Then you can view this image with preview.
  16. */
  17. #define ENABLE_OUTPUT 0
  18. #define IMAGE_OUTPUT_DIR @"/Users/ibireme/Desktop/image_out/"
  19. @implementation YYImageBenchmark {
  20. UIActivityIndicatorView *_indicator;
  21. UIView *_hud;
  22. NSMutableArray *_titles;
  23. NSMutableArray *_blocks;
  24. }
  25. - (void)viewDidLoad {
  26. [super viewDidLoad];
  27. [self initHUD];
  28. _titles = [NSMutableArray new];
  29. _blocks = [NSMutableArray new];
  30. self.title = @"Benchmark (See Logs in Xcode)";
  31. [self addCell:@"ImageIO Image Decode" selector:@selector(runImageDecodeBenchmark)];
  32. [self addCell:@"ImageIO Image Encode" selector:@selector(runImageEncodeBenchmark)];
  33. [self addCell:@"WebP Encode and Decode (Slow)" selector:@selector(runWebPBenchmark)];
  34. [self addCell:@"BPG Decode" selector:@selector(runBPGBenchmark)];
  35. [self addCell:@"Animated Image Decode" selector:@selector(runAnimatedImageBenchmark)];
  36. [self.tableView reloadData];
  37. }
  38. - (void)addCell:(NSString *)title selector:(SEL)sel {
  39. __weak typeof(self) _self = self;
  40. void (^block)(void) = ^() {
  41. if (![_self respondsToSelector:sel]) return;
  42. [_self startHUD];
  43. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  44. #pragma clang diagnostic push
  45. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  46. [_self performSelector:sel];
  47. #pragma clang diagnostic pop
  48. dispatch_async(dispatch_get_main_queue(), ^{
  49. [_self stopHUD];
  50. });
  51. });
  52. };
  53. [_titles addObject:title];
  54. [_blocks addObject:block];
  55. }
  56. - (void)dealloc {
  57. [_hud removeFromSuperview];
  58. }
  59. - (void)initHUD {
  60. _hud = [UIView new];
  61. _hud.size = CGSizeMake(130, 80);
  62. _hud.backgroundColor = [UIColor colorWithWhite:0.000 alpha:0.7];
  63. _hud.clipsToBounds = YES;
  64. _hud.layer.cornerRadius = 5;
  65. _indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
  66. _indicator.size = CGSizeMake(50, 50);
  67. _indicator.centerX = _hud.width / 2;
  68. _indicator.centerY = _hud.height / 2 - 9;
  69. [_hud addSubview:_indicator];
  70. UILabel *label = [UILabel new];
  71. label.textAlignment = NSTextAlignmentCenter;
  72. label.size = CGSizeMake(_hud.width, 20);
  73. label.text = @"See logs in Xcode";
  74. label.font = [UIFont systemFontOfSize:12];
  75. label.textColor = [UIColor whiteColor];
  76. label.centerX = _hud.width / 2;
  77. label.bottom = _hud.height - 8;
  78. [_hud addSubview:label];
  79. }
  80. - (void)startHUD {
  81. UIWindow *window = [[UIApplication sharedApplication].windows firstObject];
  82. _hud.center = CGPointMake(window.width / 2, window.height / 2);
  83. [_indicator startAnimating];
  84. [window addSubview:_hud];
  85. self.navigationController.view.userInteractionEnabled = NO;
  86. }
  87. - (void)stopHUD {
  88. [_indicator stopAnimating];
  89. [_hud removeFromSuperview];
  90. self.navigationController.view.userInteractionEnabled = YES;
  91. }
  92. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  93. [tableView deselectRowAtIndexPath:indexPath animated:YES];
  94. ((void (^)(void))_blocks[indexPath.row])();
  95. }
  96. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  97. return _titles.count;
  98. }
  99. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  100. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"YY"];
  101. if (!cell) {
  102. cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"YY"];
  103. }
  104. cell.textLabel.text = _titles[indexPath.row];
  105. return cell;
  106. }
  107. #pragma mark - Benchmark
  108. - (NSArray *)imageNames {
  109. return @[ @"dribbble", @"lena" ];
  110. }
  111. - (NSArray *)imageSizes {
  112. return @[ @64, @128, @256, @512 ];
  113. }
  114. - (NSArray *)imageSources {
  115. return @[ @"imageio", @"photoshop", @"imageoptim", @"pngcrush", @"tinypng", @"twitter", @"weibo", @"facebook" ];
  116. }
  117. - (NSArray *)imageTypes {
  118. return @[ (id)kUTTypeJPEG, (id)kUTTypeJPEG2000, (id)kUTTypeTIFF, (id)kUTTypeGIF, (id)kUTTypePNG, (id)kUTTypeBMP ];
  119. }
  120. - (NSString *)imageTypeGetExt:(id)type {
  121. static NSDictionary *map;
  122. static dispatch_once_t onceToken;
  123. dispatch_once(&onceToken, ^{
  124. map = @{(id)kUTTypeJPEG : @"jpg",
  125. (id)kUTTypeJPEG2000 : @"jp2",
  126. (id)kUTTypeTIFF : @"tif",
  127. (id)kUTTypeGIF : @"gif",
  128. (id)kUTTypePNG : @"png",
  129. (id)kUTTypeBMP : @"bmp"};
  130. });
  131. return type ? map[type] : nil;
  132. }
  133. - (NSArray *)imageTypeGetQuality:(NSString *)type {
  134. BOOL hasQuality = [type isEqualToString:(id)kUTTypeJPEG] || [type isEqualToString:(id)kUTTypeJPEG2000] || [type isEqualToString:@"webp"];
  135. 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];
  136. }
  137. - (void)runImageDecodeBenchmark {
  138. printf("==========================================\n");
  139. printf("ImageIO Decode Benchmark\n");
  140. printf("name size type quality length decode_time\n");
  141. for (NSString *imageName in self.imageNames) {
  142. for (NSNumber *imageSize in self.imageSizes) {
  143. for (NSString *imageSource in self.imageSources) {
  144. for (NSString *imageType in @[@"png", @"jpg"]) {
  145. @autoreleasepool {
  146. NSString *fileName = [NSString stringWithFormat:@"%@%@_%@",imageName, imageSize, imageSource];
  147. NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:imageType];
  148. NSData *data = filePath ? [NSData dataWithContentsOfFile:filePath] : nil;
  149. if (!data) continue;
  150. int count = 100;
  151. YYBenchmark(^{
  152. for (int i = 0; i < count; i++) {
  153. CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFTypeRef)data, NULL);
  154. CGImageRef image = CGImageSourceCreateImageAtIndex(source, 0, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(NO)});
  155. CGImageRef decoded = YYCGImageCreateDecodedCopy(image, YES);
  156. CFRelease(decoded);
  157. CFRelease(image);
  158. CFRelease(source);
  159. }
  160. }, ^(double ms) {
  161. printf("%8s %3d %3s %10s %6d %2.3f\n", imageName.UTF8String, imageSize.intValue, imageType.UTF8String, imageSource.UTF8String, (int)data.length, ms / count);
  162. });
  163. #if ENABLE_OUTPUT
  164. if ([UIDevice currentDevice].isSimulator) {
  165. NSString *outFilePath = [NSString stringWithFormat:@"%@%@.%@", IMAGE_OUTPUT_DIR, fileName, imageType];
  166. [data writeToFile:outFilePath atomically:YES];
  167. }
  168. #endif
  169. }
  170. }
  171. }
  172. }
  173. }
  174. printf("------------------------------------------\n\n");
  175. }
  176. - (void)runImageEncodeBenchmark {
  177. printf("==========================================\n");
  178. printf("ImageIO Encode Benchmark\n");
  179. printf("name size type quality length encode decode\n");
  180. for (NSString *imageName in self.imageNames) {
  181. for (NSNumber *imageSize in self.imageSizes) {
  182. NSString *fileName = [NSString stringWithFormat:@"%@%@_imageio",imageName, imageSize];
  183. NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@"png"];
  184. NSData *data = filePath ? [NSData dataWithContentsOfFile:filePath] : nil;
  185. CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFTypeRef)data, NULL);
  186. CGImageRef image = CGImageSourceCreateImageAtIndex(source, 0, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(NO)});
  187. CGImageRef decoded = YYCGImageCreateDecodedCopy(image, YES);
  188. for (NSString *uti in [self imageTypes]) {
  189. for (NSNumber *quality in [self imageTypeGetQuality:uti]) {
  190. __block int encodeCount = 0;
  191. __block double encodeTime = 0;
  192. __block long length = 0;
  193. __block CFMutableDataRef outData = NULL;
  194. __block int decodeCount = 0;
  195. __block double decodeTime = 0;
  196. while (encodeTime < 200) { //200ms
  197. YYBenchmark(^{
  198. if (outData) CFRelease(outData);
  199. outData = CFDataCreateMutable(CFAllocatorGetDefault(), 0);
  200. CGImageDestinationRef dest = CGImageDestinationCreateWithData(outData, (CFStringRef)uti, 1, NULL);
  201. NSDictionary *options = @{(id)kCGImageDestinationLossyCompressionQuality : quality };
  202. CGImageDestinationAddImage(dest, decoded, (CFDictionaryRef)options);
  203. CGImageDestinationFinalize(dest);
  204. length = CFDataGetLength(outData);
  205. CFRelease(dest);
  206. }, ^(double ms) {
  207. encodeTime += ms;
  208. encodeCount += 1;
  209. });
  210. }
  211. #if ENABLE_OUTPUT
  212. if ([UIDevice currentDevice].isSimulator) {
  213. NSString *outFilePath = [NSString stringWithFormat:@"%@%@%@_%.2f.%@", IMAGE_OUTPUT_DIR, imageName, imageSize, quality.floatValue, [self imageTypeGetExt:uti]];
  214. [((__bridge NSData *)outData) writeToFile:outFilePath atomically:YES];
  215. }
  216. #endif
  217. decodeCount = 100;
  218. YYBenchmark(^{
  219. for (int i = 0; i < decodeCount; i++) {
  220. CGImageSourceRef source = CGImageSourceCreateWithData(outData, NULL);
  221. CGImageRef image = CGImageSourceCreateImageAtIndex(source, 0, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(NO)});
  222. CGImageRef decoded = YYCGImageCreateDecodedCopy(image, NO);
  223. CFRelease(decoded);
  224. CFRelease(image);
  225. CFRelease(source);
  226. }
  227. }, ^(double ms) {
  228. decodeTime = ms;
  229. });
  230. CFRelease(outData);
  231. 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);
  232. }
  233. }
  234. CFRelease(decoded);
  235. CFRelease(image);
  236. CFRelease(source);
  237. }
  238. }
  239. printf("------------------------------------------\n\n");
  240. }
  241. - (void)runWebPBenchmark {
  242. printf("==========================================\n");
  243. printf("WebP Benchmark\n");
  244. printf("name size type quality method length encode decode\n");
  245. for (NSString *imageName in self.imageNames) {
  246. for (NSNumber *imageSize in self.imageSizes) {
  247. NSString *fileName = [NSString stringWithFormat:@"%@%@_imageio", imageName, imageSize];
  248. NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@"png"];
  249. NSData *data = filePath ? [NSData dataWithContentsOfFile:filePath] : nil;
  250. CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFTypeRef)data, NULL);
  251. CGImageRef image = CGImageSourceCreateImageAtIndex(source, 0, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache : @(NO) });
  252. CGImageRef decoded = YYCGImageCreateDecodedCopy(image, YES);
  253. for (NSNumber *lossless in @[ @YES, @NO ]) {
  254. for (NSNumber *q in [self imageTypeGetQuality:@"webp"]) {
  255. for (NSNumber *m in @[ @0, @1, @2, @3, @4, @5, @6 ]) {
  256. @autoreleasepool {
  257. __block int encodeCount = 0;
  258. __block double encodeTime = 0;
  259. __block long length = 0;
  260. __block CFDataRef webpData = NULL;
  261. int decodeCount = 100;
  262. 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
  263. while (encodeTime < 200) { // 200ms
  264. YYBenchmark( ^{
  265. if (webpData) CFRelease(webpData);
  266. webpData = YYCGImageCreateEncodedWebPData(decoded, lossless.boolValue, q.floatValue, m.intValue, YYImagePresetDefault);
  267. length = CFDataGetLength(webpData);
  268. }, ^(double ms) {
  269. encodeTime += ms;
  270. encodeCount += 1;
  271. });
  272. }
  273. #if ENABLE_OUTPUT
  274. if ([UIDevice currentDevice].isSimulator) {
  275. NSString *outFilePath = [NSString
  276. stringWithFormat:@"%@%@%@_%@_q%.2f_m%d.webp", IMAGE_OUTPUT_DIR, imageName, imageSize,
  277. lossless.boolValue ? @"lossless" : @"lossy", q.floatValue, m.intValue];
  278. [((__bridge NSData *)webpData)writeToFile:outFilePath atomically:YES];
  279. CGImageRef image = YYCGImageCreateWithWebPData(webpData, NO, NO, NO, NO);
  280. NSData *pngData = UIImagePNGRepresentation([UIImage imageWithCGImage:image]);
  281. NSString *pngOutFilePath = [NSString
  282. stringWithFormat:@"%@%@%@_%@_q%.2f_m%d.webp.png", IMAGE_OUTPUT_DIR, imageName, imageSize,
  283. lossless.boolValue ? @"lossless" : @"lossy", q.floatValue, m.intValue];
  284. [pngData writeToFile:pngOutFilePath atomically:YES];
  285. CFRelease(image);
  286. }
  287. #endif
  288. for (NSNumber *useThreads in @[ @NO, @YES ]) {
  289. for (NSNumber *bypassFiltering in @[ @NO, @YES ]) {
  290. for (NSNumber *noFancyUpsampling in @[ @NO, @YES ]) {
  291. __block double time = 0;
  292. YYBenchmark(^{
  293. for (int i = 0; i < decodeCount; i++) {
  294. CGImageRef image = YYCGImageCreateWithWebPData(webpData, YES, useThreads.boolValue, bypassFiltering.boolValue,noFancyUpsampling.boolValue);
  295. CFRelease(image);
  296. }
  297. }, ^(double ms) {
  298. time = ms;
  299. });
  300. decodeTime[useThreads.intValue << 2 | bypassFiltering.intValue << 1 |
  301. noFancyUpsampling.intValue] = time;
  302. }
  303. }
  304. }
  305. if (webpData) CFRelease(webpData);
  306. 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",
  307. imageName.UTF8String, imageSize.intValue, lossless.boolValue ? "lossless" : "lossy",
  308. q.floatValue, m.intValue, (int)length, encodeTime / encodeCount, decodeTime[0] / decodeCount,
  309. decodeTime[1] / decodeCount, decodeTime[2] / decodeCount, decodeTime[3] / decodeCount,
  310. decodeTime[4] / decodeCount, decodeTime[5] / decodeCount, decodeTime[6] / decodeCount,
  311. decodeTime[7] / decodeCount);
  312. }
  313. }
  314. }
  315. }
  316. CFRelease(decoded);
  317. CFRelease(image);
  318. CFRelease(source);
  319. }
  320. }
  321. printf("------------------------------------------\n\n");
  322. }
  323. - (void)runBPGBenchmark {
  324. printf("==========================================\n");
  325. printf("BPG Decode Benchmark\n");
  326. printf("name size quality length decode_time\n");
  327. for (NSString *imageName in self.imageNames) {
  328. for (NSNumber *imageSize in self.imageSizes) {
  329. for (NSString *quality in @[ @"lossless",@"q0",@"q5",@"q10",@"q15",@"q20",@"q25",@"q30",@"q35",@"q40",@"q45",@"q50"]) {
  330. @autoreleasepool {
  331. NSString *fileName = [NSString stringWithFormat:@"%@%@_%@",imageName, imageSize, quality];
  332. NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@"bpg"];
  333. NSData *data = filePath ? [NSData dataWithContentsOfFile:filePath] : nil;
  334. if (!data) continue;
  335. int count = 100;
  336. YYBenchmark(^{
  337. for (int i = 0; i < count; i++) {
  338. CGImageRef image = YYCGImageCreateWithBPGData((__bridge CFDataRef)data, YES);
  339. CFRelease(image);
  340. }
  341. }, ^(double ms) {
  342. printf("%8s %3d %8s %6d %2.3f\n", imageName.UTF8String, imageSize.intValue, quality.UTF8String, (int)data.length, ms / count);
  343. });
  344. #if ENABLE_OUTPUT
  345. if ([UIDevice currentDevice].isSimulator) {
  346. NSString *outFilePath = [NSString stringWithFormat:@"%@%@.bpg", IMAGE_OUTPUT_DIR,fileName];
  347. [data writeToFile:outFilePath atomically:YES];
  348. CGImageRef image = YYCGImageCreateWithBPGData((__bridge CFDataRef)data, YES);
  349. NSData *pngData = UIImagePNGRepresentation([UIImage imageWithCGImage:image]);
  350. CFRelease(image);
  351. NSString *pngOutFilePath = [NSString stringWithFormat:@"%@%@.bpg.png", IMAGE_OUTPUT_DIR,fileName];
  352. [pngData writeToFile:pngOutFilePath atomically:YES];
  353. }
  354. #endif
  355. }
  356. }
  357. }
  358. }
  359. printf("------------------------------------------\n\n");
  360. }
  361. - (void)runAnimatedImageBenchmark {
  362. printf("==========================================\n");
  363. printf("Animated Image Decode Benckmark\n");
  364. if (!kiOS8Later) {
  365. printf("APNG require iOS8 or later\n");
  366. return;
  367. }
  368. NSData *gif = [NSData dataNamed:@"ermilio.gif"];
  369. NSData *apng = [NSData dataNamed:@"ermilio.png"];
  370. NSData *webp_q85 = [NSData dataNamed:@"ermilio_q85.webp"];
  371. NSData *webp_q90 = [NSData dataNamed:@"ermilio_q90.webp"];
  372. NSData *webp_lossless = [NSData dataNamed:@"ermilio_lossless.webp"];
  373. NSData *bpg_q15 = [NSData dataNamed:@"ermilio_q15.bpg"];
  374. NSData *bpg_q20 = [NSData dataNamed:@"ermilio_q20.bpg"];
  375. NSData *bpg_lossless = [NSData dataNamed:@"ermilio_lossless.bpg"];
  376. NSArray *datas = @[gif, apng, webp_q85, webp_q90, webp_lossless, bpg_q20, bpg_q15, bpg_lossless];
  377. NSArray *names = @[@"gif", @"apng", @"webp_85", @"webp_90", @"webp_ll", @"bpg_20", @"bpg_15", @"bpg_ll"];
  378. #if ENABLE_OUTPUT
  379. if ([UIDevice currentDevice].isSimulator) {
  380. @autoreleasepool {
  381. NSString *outPath = [NSString stringWithFormat:@"%@ermilio.gif.png",IMAGE_OUTPUT_DIR];
  382. NSData *outData = UIImagePNGRepresentation([UIImage imageWithData:gif]);
  383. [outData writeToFile:outPath atomically:YES];
  384. [gif writeToFile:[NSString stringWithFormat:@"%@ermilio.gif",IMAGE_OUTPUT_DIR] atomically:YES];
  385. }
  386. @autoreleasepool {
  387. NSString *outPath = [NSString stringWithFormat:@"%@ermilio.apng.png",IMAGE_OUTPUT_DIR];
  388. NSData *outData = UIImagePNGRepresentation([UIImage imageWithData:apng]);
  389. [outData writeToFile:outPath atomically:YES];
  390. [apng writeToFile:[NSString stringWithFormat:@"%@ermilio.png",IMAGE_OUTPUT_DIR] atomically:YES];
  391. }
  392. @autoreleasepool {
  393. NSString *outPath = [NSString stringWithFormat:@"%@ermilio_q85.webp.png",IMAGE_OUTPUT_DIR];
  394. NSData *outData = UIImagePNGRepresentation([YYImageDecoder decodeImage:webp_q85 scale:1]);
  395. [outData writeToFile:outPath atomically:YES];
  396. [webp_q85 writeToFile:[NSString stringWithFormat:@"%@ermilio_q85.webp",IMAGE_OUTPUT_DIR] atomically:YES];
  397. }
  398. @autoreleasepool {
  399. NSString *outPath = [NSString stringWithFormat:@"%@ermilio_q90.webp.png",IMAGE_OUTPUT_DIR];
  400. NSData *outData = UIImagePNGRepresentation([YYImageDecoder decodeImage:webp_q90 scale:1]);
  401. [outData writeToFile:outPath atomically:YES];
  402. [webp_q90 writeToFile:[NSString stringWithFormat:@"%@ermilio_q90.webp",IMAGE_OUTPUT_DIR] atomically:YES];
  403. }
  404. @autoreleasepool {
  405. NSString *outPath = [NSString stringWithFormat:@"%@ermilio_lossless.webp.png",IMAGE_OUTPUT_DIR];
  406. NSData *outData = UIImagePNGRepresentation([YYImageDecoder decodeImage:webp_lossless scale:1]);
  407. [outData writeToFile:outPath atomically:YES];
  408. [webp_lossless writeToFile:[NSString stringWithFormat:@"%@ermilio_lossless.webp",IMAGE_OUTPUT_DIR] atomically:YES];
  409. }
  410. @autoreleasepool {
  411. NSString *outPath = [NSString stringWithFormat:@"%@ermilio_q15.bpg.png",IMAGE_OUTPUT_DIR];
  412. CGImageRef imageRef = YYCGImageCreateWithBPGData((__bridge CFDataRef)bpg_q15, NO);
  413. NSData *outData = UIImagePNGRepresentation([UIImage imageWithCGImage:imageRef]);
  414. [outData writeToFile:outPath atomically:YES];
  415. CFRelease(imageRef);
  416. [bpg_q15 writeToFile:[NSString stringWithFormat:@"%@ermilio_q15.bpg",IMAGE_OUTPUT_DIR] atomically:YES];
  417. }
  418. @autoreleasepool {
  419. NSString *outPath = [NSString stringWithFormat:@"%@ermilio_q20.bpg.png",IMAGE_OUTPUT_DIR];
  420. CGImageRef imageRef = YYCGImageCreateWithBPGData((__bridge CFDataRef)bpg_q20, NO);
  421. NSData *outData = UIImagePNGRepresentation([UIImage imageWithCGImage:imageRef]);
  422. [outData writeToFile:outPath atomically:YES];
  423. CFRelease(imageRef);
  424. [bpg_q20 writeToFile:[NSString stringWithFormat:@"%@ermilio_q20.bpg",IMAGE_OUTPUT_DIR] atomically:YES];
  425. }
  426. @autoreleasepool {
  427. NSString *outPath = [NSString stringWithFormat:@"%@ermilio_lossless.bpg.png",IMAGE_OUTPUT_DIR];
  428. CGImageRef imageRef = YYCGImageCreateWithBPGData((__bridge CFDataRef)bpg_lossless, NO);
  429. NSData *outData = UIImagePNGRepresentation([UIImage imageWithCGImage:imageRef]);
  430. [outData writeToFile:outPath atomically:YES];
  431. CFRelease(imageRef);
  432. [bpg_lossless writeToFile:[NSString stringWithFormat:@"%@ermilio_lossless.bpg",IMAGE_OUTPUT_DIR] atomically:YES];
  433. }
  434. }
  435. #endif
  436. printf("------------------------------------------\n");
  437. printf("image length\n");
  438. for (int i = 0; i < names.count; i++) {
  439. NSString *name = names[i];
  440. NSData *data = datas[i];
  441. printf("%7s %6d\n",name.UTF8String, (int)data.length);
  442. }
  443. printf("\n\n");
  444. int count = 20;
  445. int frame_num = 28;
  446. typedef void (^CoverDecodeBlock)(id src);
  447. typedef void (^SingleFrameDecodeBlock)(id src, NSUInteger index);
  448. typedef void (^AllFrameDecodeBlock)(id src, BOOL reverseOrder);
  449. /// Cover: gif/apng
  450. CoverDecodeBlock imageioCoverDecoder = ^(NSData *data){
  451. CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFTypeRef)data, NULL);
  452. CGImageRef image = CGImageSourceCreateImageAtIndex(source, 0, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(NO)});
  453. CGImageRef decoded = YYCGImageCreateDecodedCopy(image, YES);
  454. CFRelease(decoded);
  455. CFRelease(image);
  456. CFRelease(source);
  457. };
  458. /// Cover: gif/apng/webp
  459. CoverDecodeBlock yyCoverDecoder = ^(NSData *data) {
  460. @autoreleasepool {
  461. YYImageDecoder *decoder = [YYImageDecoder decoderWithData:data scale:1];
  462. [decoder frameAtIndex:0 decodeForDisplay:YES];;
  463. }
  464. };
  465. /// Cover: webp
  466. CoverDecodeBlock webpCoverDecoder = ^(NSData *data) {
  467. CGImageRef image = YYCGImageCreateWithWebPData((__bridge CFDataRef)data, YES, NO, NO, NO);
  468. CFRelease(image);
  469. };
  470. /// Cover: bpg
  471. CoverDecodeBlock bpgCoverDecoder = ^(NSData *data) {
  472. CGImageRef image = YYCGImageCreateWithBPGData((__bridge CFDataRef)data, YES);
  473. CFRelease(image);
  474. };
  475. NSArray *coverSrcs = @[@"gif imageio", gif, imageioCoverDecoder,
  476. @"gif yydecoder", gif, yyCoverDecoder,
  477. @"apng imageio", apng, imageioCoverDecoder,
  478. @"apng yydecoder", apng, yyCoverDecoder,
  479. @"webp_85 yyimage", webp_q85, webpCoverDecoder,
  480. @"webp_85 yydecocer", webp_q85, yyCoverDecoder,
  481. @"webp_90 yyimage", webp_q90, webpCoverDecoder,
  482. @"webp_90 yydecocer", webp_q90, yyCoverDecoder,
  483. @"webp_ll yyimage", webp_lossless, webpCoverDecoder,
  484. @"webp_ll yydecoder", webp_lossless, yyCoverDecoder,
  485. @"bpg_20 yyimage", bpg_q20, bpgCoverDecoder,
  486. @"bpg_15 yyimage", bpg_q20, bpgCoverDecoder,
  487. @"bpg_ll yyimage", bpg_lossless, bpgCoverDecoder,
  488. ];
  489. printf("------------------------------------------\n");
  490. printf("First frame (cover) decode\n");
  491. count = 20;
  492. for (int i = 0; i < coverSrcs.count / 3; i++) {
  493. NSString *name = coverSrcs[i * 3];
  494. id src = coverSrcs[i * 3 + 1];
  495. CoverDecodeBlock block = coverSrcs[i * 3 + 2];
  496. YYBenchmark(^{
  497. for (int r = 0; r < count; r++) {
  498. block(src);
  499. }
  500. }, ^(double ms) {
  501. printf("%s %8.3f\n",name.UTF8String, ms / count);
  502. });
  503. }
  504. printf("\n\n");
  505. /// Single: gif/apng
  506. SingleFrameDecodeBlock imagioSingleFrameDecoder = ^(id src, NSUInteger index) {
  507. CGImageSourceRef source = (__bridge CGImageSourceRef)src;
  508. CGImageRef image = CGImageSourceCreateImageAtIndex(source, index, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(NO)});
  509. CGImageRef decoded = YYCGImageCreateDecodedCopy(image, YES);
  510. CFRelease(decoded);
  511. CFRelease(image);
  512. };
  513. /// Single: gif/apng/webp
  514. SingleFrameDecodeBlock yySingleFrameDecoder = ^(YYImageDecoder *decoder, NSUInteger index) {
  515. @autoreleasepool {
  516. UIImage *img = [decoder frameAtIndex:index decodeForDisplay:YES].image;
  517. [img scale];
  518. }
  519. };
  520. NSArray *singleSrcs = @[@"gif imageio", @"gif src", imagioSingleFrameDecoder,
  521. @"gif yydecoder", @"gif", yySingleFrameDecoder,
  522. @"apng imageio", @"apng src", imagioSingleFrameDecoder,
  523. @"apng yydecoder", @"apng", yySingleFrameDecoder,
  524. @"webp_85 yydecocer", @"webp85", yySingleFrameDecoder,
  525. @"webp_90 yydecocer", @"webp90", yySingleFrameDecoder,
  526. @"webp_ll yydecoder", @"webpll", yySingleFrameDecoder,
  527. ];
  528. printf("------------------------------------------\n");
  529. printf("Single frame decode\n");
  530. count = 5;
  531. for (int i = 0; i < singleSrcs.count / 3; i++) {
  532. NSString *name = singleSrcs[i * 3];
  533. NSString *srcStr = singleSrcs[i * 3 + 1];
  534. SingleFrameDecodeBlock block = singleSrcs[i * 3 + 2];
  535. printf("%s ",name.UTF8String);
  536. for (int f = 0; f < frame_num; f++) {
  537. YYBenchmark(^{
  538. for (int r = 0; r < count; r++) {
  539. id src = NULL;
  540. if ([srcStr isEqual:@"gif src"]) {
  541. src = CFBridgingRelease(CGImageSourceCreateWithData((__bridge CFDataRef)gif, NULL));
  542. } else if ([srcStr isEqual:@"gif"]) {
  543. src = [YYImageDecoder decoderWithData:gif scale:1];
  544. } else if ([srcStr isEqual:@"apng src"]) {
  545. src = CFBridgingRelease(CGImageSourceCreateWithData((__bridge CFDataRef)apng, NULL));
  546. } else if ([srcStr isEqual:@"apng"]) {
  547. src = [YYImageDecoder decoderWithData:apng scale:1];
  548. } else if ([srcStr isEqual:@"webp85"]) {
  549. src = [YYImageDecoder decoderWithData:webp_q85 scale:1];
  550. } else if ([srcStr isEqual:@"webp90"]) {
  551. src = [YYImageDecoder decoderWithData:webp_q90 scale:1];
  552. } else if ([srcStr isEqual:@"webpll"]) {
  553. src = [YYImageDecoder decoderWithData:webp_lossless scale:1];
  554. }
  555. block(src, f);
  556. }
  557. }, ^(double ms) {
  558. printf("%8.3f ",ms / count);
  559. });
  560. }
  561. printf("\n");
  562. }
  563. printf("\n\n");
  564. /// All: gif/apng
  565. AllFrameDecodeBlock imageioAllFrameDecoder = ^(NSData *data, BOOL reverseOrder){
  566. CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFTypeRef)gif, NULL);
  567. if (reverseOrder) {
  568. for (int i = frame_num - 1; i >= 0; i--) {
  569. CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(NO)});
  570. CGImageRef decoded = YYCGImageCreateDecodedCopy(image, YES);
  571. CFRelease(decoded);
  572. CFRelease(image);
  573. }
  574. } else {
  575. for (int i = 0; i < frame_num; i++) {
  576. CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(NO)});
  577. CGImageRef decoded = YYCGImageCreateDecodedCopy(image, YES);
  578. CFRelease(decoded);
  579. CFRelease(image);
  580. }
  581. }
  582. CFRelease(source);
  583. };
  584. /// All: gif/apng/webp
  585. AllFrameDecodeBlock yyAllFrameDecoder = ^(NSData *data, BOOL reverseOrder){
  586. @autoreleasepool {
  587. YYImageDecoder *decoder = [YYImageDecoder decoderWithData:data scale:1];
  588. if (reverseOrder) {
  589. for (int i = frame_num - 1; i > 0; i--) {
  590. [decoder frameAtIndex:i decodeForDisplay:YES];
  591. }
  592. } else {
  593. for (int i = 1; i < frame_num; i++) {
  594. [decoder frameAtIndex:i decodeForDisplay:YES];
  595. }
  596. }
  597. }
  598. };
  599. /// All: bpg
  600. AllFrameDecodeBlock bpgAllFrameDecoder = ^(NSData *data, BOOL reverseOrder){
  601. @autoreleasepool {
  602. YYCGImageDecodeAllFrameInBPGData((__bridge CFDataRef)data, YES);
  603. }
  604. };
  605. NSArray *allSrcs = @[@"gif imageio", gif, imageioAllFrameDecoder,
  606. @"gif yydecoder", gif, yyAllFrameDecoder,
  607. @"apng imageio", apng, imageioAllFrameDecoder,
  608. @"apng yydecoder", apng, yyAllFrameDecoder,
  609. @"webp_85 yydecocer", webp_q85, yyAllFrameDecoder,
  610. @"webp_90 yydecocer", webp_q90, yyAllFrameDecoder,
  611. @"webp_ll yydecoder", webp_lossless, yyAllFrameDecoder,
  612. @"bpg_20 yyimage", bpg_q20, bpgAllFrameDecoder,
  613. @"bpg_15 yyimage", bpg_q20, bpgAllFrameDecoder,
  614. @"bpg_ll yyimage", bpg_lossless, bpgAllFrameDecoder,
  615. ];
  616. printf("------------------------------------------\n");
  617. printf("All frame decode\n");
  618. printf("type decoder asc desc\n");
  619. count = 5;
  620. for (int i = 0; i < allSrcs.count / 3; i++) {
  621. NSString *name = allSrcs[i * 3];
  622. id src = allSrcs[i * 3 + 1];
  623. AllFrameDecodeBlock block = allSrcs[i * 3 + 2];
  624. printf("%s ",name.UTF8String);
  625. for (NSNumber *rev in @[@NO, @YES]) {
  626. if ([name hasPrefix:@"bpg"] && rev.boolValue) continue;
  627. YYBenchmark(^{
  628. for (int r = 0; r < count; r++) {
  629. block(src, rev.boolValue);
  630. }
  631. }, ^(double ms) {
  632. printf("%8.3f ",ms / count);
  633. });
  634. }
  635. printf("\n");
  636. }
  637. printf("\n\n");
  638. }
  639. @end