结果CATiledLayer
工做的很好,性能问题解决了,并且和用GCD实现的代码量差很少。仅有一个问题在于图片加载到屏幕上后有一个明显的淡入(图14.4)。git
图14.4 加载图片以后的淡入github
咱们能够调整CATiledLayer
的fadeDuration
属性来调整淡入的速度,或者直接将整个渐变移除,可是这并无根本性地去除问题:在图片加载到准备绘制的时候总会有一个延迟,这将会致使滑动时候新图片的跳入。这并非CATiledLayer
的问题,使用GCD的版本也有这个问题。算法
即便使用上述咱们讨论的全部加载图片和缓存的技术,有时候仍然会发现实时加载大图仍是有问题。就和13章中提到的那样,iPad上一整个视网膜屏图片分辨率达到了2048x1536,并且会消耗12MB的RAM(未压缩)。第三代iPad的硬件并不能支持1/60秒的帧率加载,解压和显示这种图片。即便用后台线程加载来避免动画卡顿,仍然解决不了问题。缓存
咱们能够在加载的同时显示一个占位图片,但这并无根本解决问题,咱们能够作到更好。网络
视网膜分辨率(根据苹果市场定义)表明了人的肉眼在正常视角距离可以分辨的最小像素尺寸。可是这只能应用于静态像素。当观察一个移动图片时,你的眼睛就会对细节不敏感,因而一个低分辨率的图片和视网膜质量的图片没什么区别了。多线程
若是须要快速加载和显示移动大图,简单的办法就是欺骗人眼,在移动传送器的时候显示一个小图(或者低分辨率),而后当中止的时候再换成大图。这意味着咱们须要对每张图片存储两份不一样分辨率的副本,可是幸运的是,因为须要同时支持Retina和非Retina设备,原本这就是广泛要作到的。async
若是从远程源或者用户的相册加载没有可用的低分辨率版本图片,那就能够动态将大图绘制到较小的CGContext
,而后存储到某处以备复用。ide
为了作到图片交换,咱们须要利用UIScrollView
的一些实现UIScrollViewDelegate
协议的委托方法(和其余相似于UITableView
和UICollectionView
基于滚动视图的控件同样):工具
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
你可使用这几个方法来检测传送器是否中止滚动,而后加载高分辨率的图片。只要高分辨率图片和低分辨率图片尺寸颜色保持一致,你会很难察觉到替换的过程(确保在同一台机器使用相同的图像程序或者脚本生成这些图片)。性能
若是有不少张图片要显示,最好不要提早把全部都加载进来,而是应该当移出屏幕以后马上销毁。经过选择性的缓存,你就能够避免来回滚动时图片重复性的加载了。
缓存其实很简单:就是存储昂贵计算后的结果(或者是从闪存或者网络加载的文件)在内存中,以便后续使用,这样访问起来很快。问题在于缓存本质上是一个权衡过程 - 为了提高性能而消耗了内存,可是因为内存是一个很是宝贵的资源,因此不能把全部东西都作缓存。
什么时候将何物作缓存(作多久)并不老是很明显。幸运的是,大多状况下,iOS都为咱们作好了图片的缓存。
+imageNamed:
方法以前咱们提到使用[UIImage imageNamed:]
加载图片有个好处在于能够马上解压图片而不用等到绘制的时候。可是[UIImage imageNamed:]
方法有另外一个很是显著的好处:它在内存中自动缓存了解压后的图片,即便你本身没有保留对它的任何引用。
对于iOS应用那些主要的图片(例如图标,按钮和背景图片),使用[UIImage imageNamed:]
加载图片是最简单最有效的方式。在nib文件中引用的图片一样也是这个机制,因此你不少时候都在隐式的使用它。
可是[UIImage imageNamed:]
并不适用任何状况。它为用户界面作了优化,可是并非对应用程序须要显示的全部类型的图片都适用。有些时候你仍是要实现本身的缓存机制,缘由以下:
[UIImage imageNamed:]
方法仅仅适用于在应用程序资源束目录下的图片,可是大多数应用的许多图片都要从网络或者是用户的相机中获取,因此[UIImage imageNamed:]
就无法用了。
[UIImage imageNamed:]
缓存用来存储应用界面的图片(按钮,背景等等)。若是对照片这种大图也用这种缓存,那么iOS系统就极可能会移除这些图片来节省内存。那么在切换页面时性能就会降低,由于这些图片都须要从新加载。对传送器的图片使用一个单独的缓存机制就能够把它和应用图片的生命周期解耦。
[UIImage imageNamed:]
缓存机制并非公开的,因此你不能很好地控制它。例如,你无法作到检测图片是否在加载以前就作了缓存,不可以设置缓存大小,当图片没用的时候也不能把它从缓存中移除。
构建一个所谓的缓存系统很是困难。菲尔 卡尔顿曾经说过:“在计算机科学中只有两件难事:缓存和命名”。
若是要写本身的图片缓存的话,那该如何实现呢?让咱们来看看要涉及哪些方面:
选择一个合适的缓存键 - 缓存键用来作图片的惟一标识。若是实时建立图片,一般不太好生成一个字符串来区分别的图片。在咱们的图片传送带例子中就很简单,咱们能够用图片的文件名或者表格索引。
提早缓存 - 若是生成和加载数据的代价很大,你可能想当第一次须要用到的时候再去加载和缓存。提早加载的逻辑是应用内在就有的,可是在咱们的例子中,这也很是好实现,由于对于一个给定的位置和滚动方向,咱们就能够精确地判断出哪一张图片将会出现。
缓存失效 - 若是图片文件发生了变化,怎样才能通知到缓存更新呢?这是个很是困难的问题(就像菲尔 卡尔顿提到的),可是幸运的是当从程序资源加载静态图片的时候并不须要考虑这些。对用户提供的图片来讲(可能会被修改或者覆盖),一个比较好的方式就是当图片缓存的时候打上一个时间戳以便当文件更新的时候做比较。
缓存回收 - 当内存不够的时候,如何判断哪些缓存须要清空呢?这就须要到你写一个合适的算法了。幸运的是,对缓存回收的问题,苹果提供了一个叫作NSCache
通用的解决方案
NSCache
和NSDictionary
相似。你能够经过-setObject:forKey:
和-object:forKey:
方法分别来插入,检索。和字典不一样的是,NSCache
在系统低内存的时候自动丢弃存储的对象。
NSCache
用来判断什么时候丢弃对象的算法并无在文档中给出,可是你可使用-setCountLimit:
方法设置缓存大小,以及-setObject:forKey:cost:
来对每一个存储的对象指定消耗的值来提供一些暗示。
指定消耗数值能够用来指定相对的重建成本。若是对大图指定一个大的消耗值,那么缓存就知道这些物体的存储更加昂贵,因而当有大的性能问题的时候才会丢弃这些物体。你也能够用-setTotalCostLimit:
方法来指定全体缓存的尺寸。
NSCache
是一个广泛的缓存解决方案,咱们建立一个比传送器案例更好的自定义的缓存类。(例如,咱们能够基于不一样的缓存图片索引和当前中间索引来判断哪些图片须要首先被释放)。可是NSCache
对咱们当前的缓存需求来讲已经足够了;不必过早作优化。
使用图片缓存和提早加载的实现来扩展以前的传送器案例,而后来看看是否效果更好(见清单14.5)。
清单14.5 添加缓存
#import "ViewController.h"@interface ViewController() <UICollectionViewDataSource>@property (nonatomic, copy) NSArray *imagePaths; @property (nonatomic, weak) IBOutlet UICollectionView *collectionView;@end@implementation ViewController- (void)viewDidLoad { //set up data self.imagePaths = [[NSBundle mainBundle] pathsForResourcesOfType:@"png" inDirectory:@"Vacation Photos"]; //register cell class [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"Cell"]; }- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return [self.imagePaths count]; }- (UIImage *)loadImageAtIndex:(NSUInteger)index { //set up cache static NSCache *cache = nil; if (!cache) { cache = [[NSCache alloc] init]; } //if already cached, return immediately UIImage *image = [cache objectForKey:@(index)]; if (image) { return [image isKindOfClass:[NSNull class]]? nil: image; } //set placeholder to avoid reloading image multiple times [cache setObject:[NSNull null] forKey:@(index)]; //switch to background thread dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ //load image NSString *imagePath = self.imagePaths[index]; UIImage *image = [UIImage imageWithContentsOfFile:imagePath]; //redraw image using device context UIGraphicsBeginImageContextWithOptions(image.size, YES, 0); [image drawAtPoint:CGPointZero]; image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); //set image for correct image view dispatch_async(dispatch_get_main_queue(), ^{ //cache the image [cache setObject:image forKey:@(index)]; //display the image NSIndexPath *indexPath = [NSIndexPath indexPathForItem: index inSection:0]; UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath]; UIImageView *imageView = [cell.contentView.subviews lastObject]; imageView.image = image; }); }); //not loaded yet return nil; }- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { //dequeue cell UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath]; //add image view UIImageView *imageView = [cell.contentView.subviews lastObject]; if (!imageView) { imageView = [[UIImageView alloc] initWithFrame:cell.contentView.bounds]; imageView.contentMode = UIViewContentModeScaleAspectFit; [cell.contentView addSubview:imageView]; } //set or load image for this index imageView.image = [self loadImageAtIndex:indexPath.item]; //preload image for previous and next index if (indexPath.item < [self.imagePaths count] - 1) { [self loadImageAtIndex:indexPath.item + 1]; } if (indexPath.item > 0) { [self loadImageAtIndex:indexPath.item - 1]; } return cell; }@end
果真效果更好了!当滚动的时候虽然还有一些图片进入的延迟,可是已经很是罕见了。缓存意味着咱们作了更少的加载。这里提早加载逻辑很是粗暴,其实能够把滑动速度和方向也考虑进来,但这已经比以前没作缓存的版本好不少了。
图片加载性能取决于加载大图的时间和解压小图时间的权衡。不少苹果的文档都说PNG是iOS全部图片加载的最好格式。但这是极度误导的过期信息了。
PNG图片使用的无损压缩算法能够比使用JPEG的图片作到更快地解压,可是因为闪存访问的缘由,这些加载的时间并无什么区别。
清单14.6展现了标准的应用程序加载不一样尺寸图片所须要时间的一些代码。为了保证明验的准确性,咱们会测量每张图片的加载和绘制时间来确保考虑到解压性能的因素。另外每隔一秒重复加载和绘制图片,这样就能够取到平均时间,使得结果更加准确。
清单14.6
#import "ViewController.h"static NSString *const ImageFolder = @"Coast Photos";@interface ViewController () <UITableViewDataSource>@property (nonatomic, copy) NSArray *items; @property (nonatomic, weak) IBOutlet UITableView *tableView;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; //set up image names self.items = @[@"2048x1536", @"1024x768", @"512x384", @"256x192", @"128x96", @"64x48", @"32x24"]; }- (CFTimeInterval)loadImageForOneSec:(NSString *)path { //create drawing context to use for decompression UIGraphicsBeginImageContext(CGSizeMake(1, 1)); //start timing NSInteger imagesLoaded = 0; CFTimeInterval endTime = 0; CFTimeInterval startTime = CFAbsoluteTimeGetCurrent(); while (endTime - startTime < 1) { //load image UIImage *image = [UIImage imageWithContentsOfFile:path]; //decompress image by drawing it [image drawAtPoint:CGPointZero]; //update totals imagesLoaded ++; endTime = CFAbsoluteTimeGetCurrent(); } //close context UIGraphicsEndImageContext(); //calculate time per image return (endTime - startTime) / imagesLoaded; }- (void)loadImageAtIndex:(NSUInteger)index { //load on background thread so as not to //prevent the UI from updating between runs dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ //setup NSString *fileName = self.items[index]; NSString *pngPath = [[NSBundle mainBundle] pathForResource:filename ofType:@"png" inDirectory:ImageFolder]; NSString *jpgPath = [[NSBundle mainBundle] pathForResource:filename ofType:@"jpg" inDirectory:ImageFolder]; //load NSInteger pngTime = [self loadImageForOneSec:pngPath] * 1000; NSInteger jpgTime = [self loadImageForOneSec:jpgPath] * 1000; //updated UI on main thread dispatch_async(dispatch_get_main_queue(), ^{ //find table cell and update NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0]; UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath]; cell.detailTextLabel.text = [NSString stringWithFormat:@"PNG: %03ims JPG: %03ims", pngTime, jpgTime]; }); }); }- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.items count]; }- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { //dequeue cell UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"Cell"]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleValue1 reuseIdentifier:@"Cell"]; } //set up cell NSString *imageName = self.items[indexPath.row]; cell.textLabel.text = imageName; cell.detailTextLabel.text = @"Loading..."; //load image [self loadImageAtIndex:indexPath.row]; return cell; }@end
PNG和JPEG压缩算法做用于两种不一样的图片类型:JPEG对于噪点大的图片效果很好;可是PNG更适合于扁平颜色,锋利的线条或者一些渐变色的图片。为了让测评的基准更加公平,咱们用一些不一样的图片来作实验:一张照片和一张彩虹色的渐变。JPEG版本的图片都用默认的Photoshop60%“高质量”设置编码。结果见图片14.5。
图14.5 不一样类型图片的相对加载性能
如结果所示,相对于不友好的PNG图片,相同像素的JPEG图片老是比PNG加载更快,除非一些很是小的图片、但对于友好的PNG图片,一些中大尺寸的图效果仍是很好的。
因此对于以前的图片传送器程序来讲,JPEG会是个不错的选择。若是用JPEG的话,一些多线程和缓存策略都不必了。
但JPEG图片并非全部状况都适用。若是图片须要一些透明效果,或者压缩以后细节损耗不少,那就该考虑用别的格式了。苹果在iOS系统中对PNG和JPEG都作了一些优化,因此普通状况下都应该用这种格式。也就是说在一些特殊的状况下才应该使用别的格式。
对于包含透明的图片来讲,最好是使用压缩透明通道的PNG图片和压缩RGB部分的JPEG图片混合起来加载。这就对任何格式都适用了,并且不管从质量仍是文件尺寸仍是加载性能来讲都和PNG和JPEG的图片相近。相关分别加载颜色和遮罩图片并在运行时合成的代码见14.7。
清单14.7 从PNG遮罩和JPEG建立的混合图片
#import "ViewController.h"@interface ViewController () @property (nonatomic, weak) IBOutlet UIImageView *imageView;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; //load color image UIImage *image = [UIImage imageNamed:@"Snowman.jpg"]; //load mask image UIImage *mask = [UIImage imageNamed:@"SnowmanMask.png"]; //convert mask to correct format CGColorSpaceRef graySpace = CGColorSpaceCreateDeviceGray(); CGImageRef maskRef = CGImageCreateCopyWithColorSpace(mask.CGImage, graySpace); CGColorSpaceRelease(graySpace); //combine images CGImageRef resultRef = CGImageCreateWithMask(image.CGImage, maskRef); UIImage *result = [UIImage imageWithCGImage:resultRef]; CGImageRelease(resultRef); CGImageRelease(maskRef); //display result self.imageView.image = result; }@end
对每张图片都使用两个独立的文件确实有些累赘。JPNG的库(https://github.com/nicklockwood/JPNG)对这个技术提供了一个开源的能够复用的实现,而且添加了直接使用+imageNamed:
和+imageWithContentsOfFile:
方法的支持。
除了JPEG和PNG以外iOS还支持别的一些格式,例如TIFF和GIF,可是因为他们质量压缩得更厉害,性能比JPEG和PNG糟糕的多,因此大多数状况并不用考虑。
可是iOS以后,苹果低调添加了对JPEG 2000图片格式的支持,因此大多数人并不知道。它甚至并不被Xcode很好的支持 - JPEG 2000图片都没在Interface Builder中显示。
可是JPEG 2000图片在(设备和模拟器)运行时会有效,并且比JPEG质量更好,一样也对透明通道有很好的支持。可是JPEG 2000图片在加载和显示图片方面明显要比PNG和JPEG慢得多,因此对图片大小比运行效率更敏感的时候,使用它是一个不错的选择。
但仍然要对JPEG 2000保持关注,由于在后续iOS版本说不定就对它的性能作提高,可是在现阶段,混合图片对更小尺寸和质量的文件性能会更好。
当前市场的每一个iOS设备都使用了Imagination Technologies PowerVR图像芯片做为GPU。PowerVR芯片支持一种叫作PVRTC(PowerVR Texture Compression)的标准图片压缩。
和iOS上可用的大多数图片格式不一样,PVRTC不用提早解压就能够被直接绘制到屏幕上。这意味着在加载图片以后不须要有解压操做,因此内存中的图片比其余图片格式大大减小了(这取决于压缩设置,大概只有1/60那么大)。
可是PVRTC仍然有一些弊端:
尽管加载的时候消耗了更少的RAM,PVRTC文件比JPEG要大,有时候甚至比PNG还要大(这取决于具体内容),由于压缩算法是针对于性能,而不是文件尺寸。
PVRTC必需要是二维正方形,若是源图片不知足这些要求,那必需要在转换成PVRTC的时候强制拉伸或者填充空白空间。
质量并非很好,尤为是透明图片。一般看起来更像严重压缩的JPEG文件。
PVRTC不能用Core Graphics绘制,也不能在普通的UIImageView
显示,也不能直接用做图层的内容。你必需要用做OpenGL纹理加载PVRTC图片,而后映射到一对三角板来在CAEAGLLayer
或者GLKView
中显示。
建立一个OpenGL纹理来绘制PVRTC图片的开销至关昂贵。除非你想把全部图片绘制到一个相同的上下文,否则这彻底不能发挥PVRTC的优点。
PVRTC使用了一个不对称的压缩算法。尽管它几乎当即解压,可是压缩过程至关漫长。在一个现代快速的桌面Mac电脑上,它甚至要消耗一分钟甚至更多来生成一个PVRTC大图。所以在iOS设备上最好不要实时生成。
若是你愿意使用OpehGL,并且即便提早生成图片也能忍受得了,那么PVRTC将会提供相对于别的可用格式来讲很是高效的加载性能。好比,能够在主线程1/60秒以内加载并显示一张2048×2048的PVRTC图片(这已经足够大来填充一个视网膜屏幕的iPad了),这就避免了不少使用线程或者缓存等等复杂的技术难度。
Xcode包含了一些命令行工具例如texturetool来生成PVRTC图片,可是用起来很不方便(它存在于Xcode应用程序束中),并且很受限制。一个更好的方案就是使用Imagination Technologies PVRTexTool,能够从http://www.imgtec.com/powervr/insider/sdkdownloads免费得到。
安装了PVRTexTool以后,就可使用以下命令在终端中把一个合适大小的PNG图片转换成PVRTC文件:
/Applications/Imagination/PowerVR/GraphicsSDK/PVRTexTool/CL/OSX_x86/PVRTexToolCL -i {input_file_name}.png -o {output_file_name}.pvr -legacypvr -p -f PVRTC1_4 -q pvrtcbest
清单14.8的代码展现了加载和显示PVRTC图片的步骤(第6章CAEAGLLayer
例子代码改动而来)。
清单14.8 加载和显示PVRTC图片
#import "ViewController.h" #import <QuartzCore/QuartzCore.h> #import <GLKit/GLKit.h>@interface ViewController () @property (nonatomic, weak) IBOutlet UIView *glView; @property (nonatomic, strong) EAGLContext *glContext; @property (nonatomic, strong) CAEAGLLayer *glLayer; @property (nonatomic, assign) GLuint framebuffer; @property (nonatomic, assign) GLuint colorRenderbuffer; @property (nonatomic, assign) GLint framebufferWidth; @property (nonatomic, assign) GLint framebufferHeight; @property (nonatomic, strong) GLKBaseEffect *effect; @property (nonatomic, strong) GLKTextureInfo *textureInfo;@end@implementation ViewController- (void)setUpBuffers { //set up frame buffer glGenFramebuffers(1, &_framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer); //set up color render buffer glGenRenderbuffers(1, &_colorRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderbuffer); [self.glContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.glLayer]; glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_framebufferWidth); glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_framebufferHeight); //check success if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { NSLog(@"Failed to make complete framebuffer object: %i", glCheckFramebufferStatus(GL_FRAMEBUFFER)); } }- (void)tearDownBuffers { if (_framebuffer) { //delete framebuffer glDeleteFramebuffers(1, &_framebuffer); _framebuffer = 0; } if (_colorRenderbuffer) { //delete color render buffer glDeleteRenderbuffers(1, &_colorRenderbuffer); _colorRenderbuffer = 0; } }- (void)drawFrame { //bind framebuffer & set viewport glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer); glViewport(0, 0, _framebufferWidth, _framebufferHeight); //bind shader program [self.effect prepareToDraw]; //clear the screen glClear(GL_COLOR_BUFFER_BIT); glClearColor(0.0, 0.0, 0.0, 0.0); //set up vertices GLfloat vertices[] = { -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f }; //set up colors GLfloat texCoords[] = { 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f }; //draw triangle glEnableVertexAttribArray(GLKVertexAttribPosition); glEnableVertexAttribArray(GLKVertexAttribTexCoord0); glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, 0, vertices); glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 0, texCoords); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); //present render buffer glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer); [self.glContext presentRenderbuffer:GL_RENDERBUFFER]; }- (void)viewDidLoad { [super viewDidLoad]; //set up context self.glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; [EAGLContext setCurrentContext:self.glContext]; //set up layer self.glLayer = [CAEAGLLayer layer]; self.glLayer.frame = self.glView.bounds; self.glLayer.opaque = NO; [self.glView.layer addSublayer:self.glLayer]; self.glLayer.drawableProperties = @{kEAGLDrawablePropertyRetainedBacking: @NO, kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8}; //load texture glActiveTexture(GL_TEXTURE0); NSString *imageFile = [[NSBundle mainBundle] pathForResource:@"Snowman" ofType:@"pvr"]; self.textureInfo = [GLKTextureLoader textureWithContentsOfFile:imageFile options:nil error:NULL]; //create texture GLKEffectPropertyTexture *texture = [[GLKEffectPropertyTexture alloc] init]; texture.enabled = YES; texture.envMode = GLKTextureEnvModeDecal; texture.name = self.textureInfo.name; //set up base effect self.effect = [[GLKBaseEffect alloc] init]; self.effect.texture2d0.name = texture.name; //set up buffers [self setUpBuffers]; //draw frame [self drawFrame]; }- (void)viewDidUnload { [self tearDownBuffers]; [super viewDidUnload]; }- (void)dealloc { [self tearDownBuffers]; [EAGLContext setCurrentContext:nil]; }@end
如你所见,很是不容易,若是你对在常规应用中使用PVRTC图片很感兴趣的话(例如基于OpenGL的游戏),能够参考一下GLView的库(https://github.com/nicklockwood/GLView),它提供了一个简单的GLImageView类,从新实现了UIImageView的各类功能,但同时提供了PVRTC图片,而不须要你写任何OpenGL代码。