利用预渲染加速iOS设备的图像显示
来源:互联网 发布:军装照片软件 编辑:程序博客网 时间:2024/06/17 00:29
转:点击打开链接
最近在做一个UITableView的例子,发现滚动时的性能还不错。但来回滚动时,第一次显示的图像不如再次显示的图像流畅,出现前会有稍许的停顿感。于是我猜想显示过的图像肯定是被缓存起来了,查了下文档后发现果然如此。后来在《Improving Image Drawing Performance on iOS》一文中找到了一些提示:原来在显示图像时,解压和重采样会消耗很多CPU时间;而如果预先在一个bitmap context里画出图像,再缓存这个图像,就能省去这些繁重的工作了。接着我就写了个例子程序来验证:
// ImageView.h#import <UIKit/UIKit.h>@interface ImageView : UIView { UIImage *image;}@property (retain, nonatomic) UIImage *image;@end
// ImageView.m#include <mach/mach_time.h>#import "ImageView.h"@implementation ImageView#define LABEL_TAG 1static const CGRect imageRect = {{0, 0}, {100, 100}};static const CGPoint imagePoint = {0, 0};@synthesize image;- (void)awakeFromNib { if (!self.image) { self.image = [UIImage imageNamed:@"random.jpg"]; }}- (void)drawRect:(CGRect)rect { if (CGRectEqualToRect(rect, imageRect)) { uint64_t start = mach_absolute_time(); [image drawAtPoint:imagePoint]; uint64_t drawTime = mach_absolute_time() - start; NSString *text = [[NSString alloc] initWithFormat:@"%lld", drawTime]; UILabel *label = (UILabel *)[self viewWithTag:LABEL_TAG]; label.text = text; [text release]; }}- (void)dealloc { [super dealloc]; [image release];}@end控制器的代码我就不列出了,就是点按钮时,更新view(调用[self.view setNeedsDisplayInRect:imageRect]),画出一张图,并在label中显示消耗的时间。值得一提的是,在模拟器上可以直接用clock()函数获得微秒级的精度,但iOS设备上精度为10毫秒。于是我找到了mach_absolute_time(),它在Mac和iOS设备上都有纳秒级的精度。测试用的是一张200x200像素的JPEG图像,命名时加了@2x,在iPhone 4上第一次显示时花了约300微秒,再次显示约65微秒。接下来就是见证奇迹的时刻了,把这段代码加入程序:
static const CGSize imageSize = {100, 100};- (void)awakeFromNib { if (!self.image) { self.image = [UIImage imageNamed:@"random.jpg"]; if (NULL != UIGraphicsBeginImageContextWithOptions) UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0); else UIGraphicsBeginImageContext(imageSize); [image drawInRect:imageRect]; self.image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); }}这里需要判断一下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变量:
static CGImageRef imageRef;再在awakeFromNib中设置一下它的值:
imageRef = self.image.CGImage;最后在drawRect:中绘制:
CGContextRef context = UIGraphicsGetCurrentContext();CGContextDrawImage(context, imageRect, imageRef);搞定运行一下,发现时间增加到33微秒左右了,而且图像还上下颠倒了⋯
这个原因是UIKit和Core Graphics的坐标系y轴是相反的,于是加上2行代码来修正:
CGContextRef context = UIGraphicsGetCurrentContext();CGContextTranslateCTM(context, 0, 100);CGContextScaleCTM(context, 1, -1);CGContextDrawImage(context, imageRect, imageRef);这下图像终于正常显示了,时间缩短到了14微秒左右,成效不大,看来直接用-drawAtPoint:和-drawInRect:也足够好了。
当然,这个例子正确的做法是用viewDidLoad或loadView,不过我懒得列出控制器代码,所以就放awakeFromNib里了。
2011年9月22日更新勘误:
刚看到Mach Absolute Time Units这篇Q&A,发现mach_absolute_time()的单位是Mach absolute time unit,而不是纳秒。它们之间的换算关系和CPU相关,不是一个常量。最简单的办法是用CoreServices框架的AbsoluteToNanoseconds和AbsoluteToDuration函数来转换。此外也可以用mach_timebase_info函数来获取这个比值。我在iPhone 4上测得的numer和denom分别为125和3,比值约为42,因此本文所述的时间都需要乘以42。
0 0
- 利用预渲染加速iOS设备的图像显示
- 利用预渲染加速iOS设备的图像显示
- 利用预渲染加速iOS设备的图像显示
- 利用预渲染加速iOS设备的图像显示
- 利用预渲染加速iOS设备的图像显示
- 利用预渲染加速iOS设备的图像显示
- 利用预渲染加速iOS设备的图像显示
- 利用预渲染加速iOS设备的图像显示
- 利用预渲染加速iOS设备的图像显示
- 利用预渲染加速iOS设备的图像显示
- 利用预渲染加速iOS设备的图像显示
- 利用预渲染加速iOS设备的图像显示
- 利用预渲染加速iOS设备的图像显示
- 利用预渲染加速iOS设备的图像显示
- 利用预渲染加速iOS设备的图像显示
- 利用预渲染加速iOS设备的图像显示
- 利用预渲染加速iOS设备的图像显示
- 利用预渲染加速iOS设备的图像显示
- Codeforces 400D Dima and Bacteria(Floyd+并查集)
- 黑马程序员_交通灯管理系统需求与分析
- provider: SQL 网络接口, error: 26 - 定位指定的服务器/实例时出错
- Java Socket通信及心跳包
- 黑马程序员---模板方法设计模式
- 利用预渲染加速iOS设备的图像显示
- ios 模拟器未能安装此应用程序
- iOS状态条
- 黑马程序员_交通灯管理系统代码实现
- c#开发_Dev的关于XtraGrid的使用(GridControl小结)_Second
- 多条记录横着显示
- 解决中文乱码的问题要考虑的8个地方|(utf-8,用于抛砖引玉)
- ios项目如何实现版本更新?
- 医学学习笔记-血压