YYImageBenchmark.m 34 KB

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