利用预渲染加速iOS设备的图像显示
最近在做一个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 1
static 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。
利用预渲染加速iOS设备的图像显示的更多相关文章
- iOS 预渲染加速图像显示
使用 UITableView 时,发现滚动时的性能还不错,但来回滚动时,第一次显示的图像不如再次显示的图像流畅,出现前会有稍许的停顿感. 于是猜想显示过的图像肯定是被缓存起来了,查了下文档后发现果然如 ...
- Windows下利用Chrome调试IOS设备页面
本文介绍如何在 Windows 系统中连接 iOS设备 并对 Web 页面进行真机调试 必须前提 iOS设备.数据线 Node.js 环境 Chrome 浏览器 环境准备 安装Node环境 参考Nod ...
- 前端性能和加载体验优化实践(附:PWA、离线包、内存优化、预渲染)
一.背景:页面为何会卡? 1.1 等待时间长(性能) 项目本身包/第三方脚本比较大. JavaScript 执行阻塞页面加载. 图片体积大且多. 特别是对于首屏资源加载中的白屏时间,用户等待的时间就越 ...
- 如何利用Pre.im分发iOS测试包
大众创新万众创业,在移动互联网的风口,移动APP开发与测试发展方兴未艾,受到了越来越多的重视.相较 iOS,Android 的开发环境更加开放.Android 开发者要测试应用时,只需发个 APK 安 ...
- 【转】iOS设备的UDID是什么?苹果为什么拒绝获取iOS设备UDID的应用?如何替代UDID?
本文讲诉的主要是为什么苹果2011年8月发布iOS 5后就开始拒绝App获取设备的UDID以及UDID替补方案,特别提醒开发者苹果App Store禁止访问UDID的应用上架(相关推荐:APP被苹果A ...
- 不通过App Store,在iOS设备上直接安装应用程序(转)
今天在iOS设备上安装天翼云存储app,在safari上直接打开http://cloud.189.cn/wap/index.jsp,点击“点击免费安装”,如下图: 神奇的事情发生了,设备上直接下载ap ...
- 关于overflow-y:scroll ios设备不流畅的问题
最近做双创项目的时候因为页面有很多数据显示,所以打算让它Y轴方向滚动条的形式展现,但在测试阶段发现IOS设备滑动效果非常不理想: search by google之后找到解决办法: -webkit-o ...
- ios设备突破微信小视频6S限制的方法
刷微信朋友圈只发文字和图片怎能意犹未竟,微信小视频是一个很好的补充,音视频到位,流行流行最流行.但小视频时长不能超过6S,没有滤镜等是很大的遗憾.but有人突破限制玩出了花样,用ios设备在朋友圈晒出 ...
- iOS不越狱装收费App——注册iOS设备为开发者工具
额,这篇教程主要是我写下来用于总结注册iOS设备和用iResign安装App的过程,想要不越狱安装App当然有办法,但是有几个前提--你是一个Apple开发者,或者你有个朋友是App的开发者.如果没有 ...
随机推荐
- 用Visio画UML顺序图
1.顺序图 顺序图又称为时序图,顾名思义,它着重表现的是对象间消息传递的时间顺序.顺序图描述的对象也是一个用例,即一组行为操作,而它表现的是这组行为的先后关系(纵坐标),以及每个行为是属于哪个对象的( ...
- UVa 10763 (STL) Foreign Exchange
看到大神说location的值不会超过1000,所以这就简单很多了,用一个deg数组记录下来每个点的度,出度-1,入读+1这样. 最后判断每个点的度是否为0即可. 至于为什么会这样,据说是套数据套出来 ...
- LA 4794 Sharing Chocolate
大白书中的题感觉一般都比较难,能理解书上代码就已经很不错了 按照经验,一般数据较小的题目,都有可能是用状态压缩来解决的 题意:问一个面积为x×y的巧克力,能否切若干刀,将其切成n块面积为A1,A2,, ...
- POJ2886 Who Gets the Most Candies? 线段树 反素数
题意:有一群小朋友围成一个环,编号1,2,3…N.每个人手上握着一个非0的数字,首先第K个人出列,然后看他手上的数字,假设为m,则从下一个开始第m个人出列,一直如此.并设i为小于等于N的最大反素数,问 ...
- 一些纯css3写的公司logo
随着对css3了解得越深入,越来越发现了css3的强大.css3不但能完成一些基本的特效如圆角阴影等,还能借助动画技术实现一些复杂的动画,能替代很多以前js才能完成的工作,css3的作用还不止于此 ...
- Ant编译环境
使用ant编译有关于C compiler(cc task)的时候,需要确认vs的环境有无配置, 否则会遇到形形色色的问题,比如各种 vs编译工具找不到的错误, 一个比较简单的解决方案,就是在执行ant ...
- Heritrix源码分析(二) 配置文件order.xml介绍(转)
本博客属原创文章,欢迎转载!转载请务必注明出处:http://guoyunsky.iteye.com/blog/613412 本博客已迁移到本人独立博客: http://www.yun5u. ...
- 【转】Github轻松上手2-如何使用命令行创建和管理repo
转自:http://blog.sina.com.cn/s/blog_4b55f6860100zzhd.html 如果你对这种怀旧的方式很感冒,不妨参考这里: http://help.github.co ...
- Linux获取时间日期方法
linux中用shell获取昨天.明天或多天前的日期:在Linux中对man date -d 参数说的比较模糊,以下举例进一步说明:# -d, --date=STRING display time d ...
- elasticsearch-head 的搭建
elasticsearch-head 全部是js和html5写的,elasticsearch 全部都是http的接口, 这样,只需要简单地本地配置一个虚拟站点,就可以搭建 elasticsearch ...