結果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代碼。