利用预渲染加速iOS设备的图像显示

移动开发 iOS
本文将讲述《利用预渲染加速iOS设备的图像显示》。在iOS平台上开发UITableView的滚动性能还不错,但来回滚动时,第一次显示的图像不如再次显示的图像流畅,出现前会有稍许的停顿感。作者教给我们一个利用预渲染加速iOS设备的图像显示方法来解决这个问题。

最近在做一个UITableView的例子,发现滚动时的性能还不错。但来回滚动时,第一次显示的图像不如再次显示的图像流畅,出现前会有稍许的停顿感。

于是我猜想显示过的图像肯定是被缓存起来了,查了下文档后发现果然如此。

后来在别的文章中找到了一些提示:原来在显示图像时,解压和重采样会消耗很多CPU时间;而如果预先在一个bitmap context里画出图像,再缓存这个图像,就能省去这些繁重的工作了。

接着我就写了个例子程序来验证:

  1. //  ImageView.h 
  2.  
  3. #import <UIKit/UIKit.h> 
  4.  
  5.  
  6. @interface ImageView : UIView { 
  7.     UIImage *image; 
  8.  
  9. @property (retain, nonatomic) UIImage *image; 
  10.  
  11. @end 
  1. //  ImageView.m 
  2.  
  3. #include <mach/mach_time.h> 
  4. #import "ImageView.h" 
  5.  
  6.  
  7. @implementation ImageView 
  8.  
  9. #define LABEL_TAG 1 
  10.  
  11. static const CGRect imageRect = {{0, 0}, {100, 100}}; 
  12. static const CGPoint imagePoint = {0, 0}; 
  13.  
  14. @synthesize image; 
  15.  
  16. - (void)awakeFromNib { 
  17.     if (!self.image) { 
  18.         self.image = [UIImage imageNamed:@"random.jpg"]; 
  19.     } 
  20.  
  21. - (void)drawRect:(CGRect)rect { 
  22.     if (CGRectEqualToRect(rect, imageRect)) { 
  23.         uint64_t start = mach_absolute_time(); 
  24.         [image drawAtPoint:imagePoint]; 
  25.         uint64_t drawTime = mach_absolute_time() - start; 
  26.          
  27.         NSString *text = [[NSString alloc] initWithFormat:@"%ld", drawTime]; 
  28.         UILabel *label = (UILabel *)[self viewWithTag:LABEL_TAG]; 
  29.         label.text = text; 
  30.         [text release]; 
  31.     } 
  32.  
  33. - (void)dealloc { 
  34.     [super dealloc]; 
  35.     [image release]; 
  36.  
  37. @end 

控制器的代码我就不列出了,就是点按钮时,更新view(调用[self.view setNeedsDisplayInRect:imageRect]),画出一张图,并在label中显示消耗的时间。

值得一提的是,在模拟器上可以直接用clock()函数获得微秒级的精度,但iOS设备上精度为10毫秒。于是我找到了mach_absolute_time(),它在Mac和iOS设备上都有纳秒级的精度。

测试用的是一张200x200像素的JPEG图像,命名时加了@2x,在iPhone 4上第一次显示时花了约300微秒,再次显示约65微秒。

接下来就是见证奇迹的时刻了,把这段代码加入程序:

  1. static const CGSize imageSize = {100, 100}; 
  2.  
  3. - (void)awakeFromNib { 
  4.     if (!self.image) { 
  5.         self.image = [UIImage imageNamed:@"random.jpg"]; 
  6.         if (NULL != UIGraphicsBeginImageContextWithOptions) 
  7.             UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0); 
  8.         else 
  9.             UIGraphicsBeginImageContext(imageSize); 
  10.         [image drawInRect:imageRect]; 
  11.         self.image = UIGraphicsGetImageFromCurrentImageContext(); 
  12.         UIGraphicsEndImageContext(); 
  13.     } 

这里需要判断一下UIGraphicsBeginImageContextWithOptions是否为NULL,因为它是iOS 4.0才加入的。

由于JPEG图像是不透明的,所以第二个参数就设为YES。

第三个参数是缩放比例,iPhone 4是2.0,其他是1.0。虽然这里可以用[UIScreen mainScreen].scale来获取,但实际上设为0后,系统就会自动设置正确的比例了。

值得一提的是,图像本身也有缩放比例,普通的图像是1.0(除了UIImage imageNamed:外,大部分API都只能获得这种图像,而且缩放比例是不可更改的),高清图像是2.0。图像的点和屏幕的像素就是依靠2者的缩放比例来计算的,例如普通图像在视网膜显示屏上是1:4,而高清图像在视网膜显示屏上则是1:1。

接下来的drawInRect:把图像画到了当前的image context里,这时就完成了解压缩和重采样的工作了。然后再从image context里获取新的image,这个image的缩放比例也能正确地和设备匹配。

再点下按钮,发现时间已经缩短到12微秒左右了,之后的画图稳定在15微秒左右。

还能更快吗?让我们来试试Core Graphics。

先定义一个全局的CGImageRef变量:

  1. static CGImageRef imageRef; 

再在awakeFromNib中设置一下它的值:

  1. imageRef = self.image.CGImage; 

最后在drawRect:中绘制:

  1. CGContextRef context = UIGraphicsGetCurrentContext(); 
  2. CGContextDrawImage(context, imageRect, imageRef); 

搞定运行一下,发现时间增加到33微秒左右了,而且图像还上下颠倒了⋯

这个原因是UIKit和Core Graphics的坐标系y轴是相反的,于是加上2行代码来修正:

  1. CGContextRef context = UIGraphicsGetCurrentContext(); 
  2. CGContextTranslateCTM(context, 0, 100); 
  3. CGContextScaleCTM(context, 1, -1); 
  4. CGContextDrawImage(context, imageRect, imageRef); 

这下图像终于正常显示了,时间缩短到了14微秒左右,成效不大,看来直接用-drawAtPoint:和-drawInRect:也足够好了。当然,这个例子正确的做法是用viewDidLoad或loadView,不过我懒得列出控制器代码,所以就放awakeFromNib里了。

责任编辑:佚名 来源: keakon的涂鸦馆
相关推荐

2011-03-08 09:28:10

iOS4.3iOSiPad

2010-08-03 13:56:57

路由器连接

2014-11-11 10:05:04

2011-07-29 11:04:52

2012-04-24 23:31:41

iOS

2011-03-25 11:25:26

Cacti图像

2012-08-07 09:06:57

iOS SDK使用指南UIDeviceNSLocale

2017-02-20 19:25:54

Python 图像处理

2015-06-08 09:06:53

DevOps软件交付

2013-08-29 11:21:19

Firefox OSiOSAndroid

2012-04-24 15:07:49

HTML5

2023-10-07 15:51:56

2022-03-18 08:59:45

TestFlight苹果漏洞

2009-07-31 18:28:46

实现C#显示图像

2015-12-23 09:16:33

ios动画渲染机制

2015-12-30 14:16:05

iOS动画视图渲染

2012-05-25 09:14:31

虚拟化虚拟设备桌面虚拟化

2011-08-22 16:39:15

iOS内存

2014-04-03 14:15:45

XenDesktopWindows XP迁

2021-09-24 21:13:02

数字化转型首席执行官技术
点赞
收藏

51CTO技术栈公众号