W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
有些時候你可能需要繪制一個很大的圖片,常見的例子就是一個高像素的照片或者是地球表面的詳細(xì)地圖。iOS應(yīng)用通暢運(yùn)行在內(nèi)存受限的設(shè)備上,所以讀取整個圖片到內(nèi)存中是不明智的。載入大圖可能會相當(dāng)?shù)芈?,那些對你看上去比較方便的做法(在主線程調(diào)用UIImage
的-imageNamed:
方法或者-imageWithContentsOfFile:
方法)將會阻塞你的用戶界面,至少會引起動畫卡頓現(xiàn)象。
能高效繪制在iOS上的圖片也有一個大小限制。所有顯示在屏幕上的圖片最終都會被轉(zhuǎn)化為OpenGL紋理,同時OpenGL有一個最大的紋理尺寸(通常是20482048,或40964096,這個取決于設(shè)備型號)。如果你想在單個紋理中顯示一個比這大的圖,即便圖片已經(jīng)存在于內(nèi)存中了,你仍然會遇到很大的性能問題,因為Core Animation強(qiáng)制用CPU處理圖片而不是更快的GPU(見第12章『速度的曲調(diào)』,和第13章『高效繪圖』,它更加詳細(xì)地解釋了軟件繪制和硬件繪制)。
CATiledLayer
為載入大圖造成的性能問題提供了一個解決方案:將大圖分解成小片然后將他們單獨按需載入。讓我們用實驗來證明一下。
這個示例中,我們將會從一個2048*2048分辨率的雪人圖片入手。為了能夠從CATiledLayer
中獲益,我們需要把這個圖片裁切成許多小一些的圖片。你可以通過代碼來完成這件事情,但是如果你在運(yùn)行時讀入整個圖片并裁切,那CATiledLayer
這些所有的性能優(yōu)點就損失殆盡了。理想情況下來說,最好能夠逐個步驟來實現(xiàn)。
清單6.11 演示了一個簡單的Mac OS命令行程序,它用CATiledLayer
將一個圖片裁剪成小圖并存儲到不同的文件中。
清單6.11 裁剪圖片成小圖的終端程序
#import
int main(int argc, const char * argv[])
{
@autoreleasepool{
?//handle incorrect arguments
if (argc < 2) {
NSLog(@"TileCutter arguments: inputfile");
return 0;
}
//input file
NSString *inputFile = [NSString stringWithCString:argv[1] encoding:NSUTF8StringEncoding];
//tile size
CGFloat tileSize = 256; //output path
NSString *outputPath = [inputFile stringByDeletingPathExtension];
//load image
NSImage *image = [[NSImage alloc] initWithContentsOfFile:inputFile];
NSSize size = [image size];
NSArray *representations = [image representations];
if ([representations count]){
NSBitmapImageRep *representation = representations[0];
size.width = [representation pixelsWide];
size.height = [representation pixelsHigh];
}
NSRect rect = NSMakeRect(0.0, 0.0, size.width, size.height);
CGImageRef imageRef = [image CGImageForProposedRect:&rect context:NULL hints:nil];
//calculate rows and columns
NSInteger rows = ceil(size.height / tileSize);
NSInteger cols = ceil(size.width / tileSize);
//generate tiles
for (int y = 0; y < rows; ++y) {
for (int x = 0; x < cols; ++x) {
//extract tile image
CGRect tileRect = CGRectMake(x*tileSize, y*tileSize, tileSize, tileSize);
CGImageRef tileImage = CGImageCreateWithImageInRect(imageRef, tileRect);
//convert to jpeg data
NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:tileImage];
NSData *data = [imageRep representationUsingType: NSJPEGFileType properties:nil];
CGImageRelease(tileImage);
//save file
NSString *path = [outputPath stringByAppendingFormat: @"_%02i_%02i.jpg", x, y];
[data writeToFile:path atomically:NO];
}
}
}
return 0;
}
這個程序?qū)?0482048分辨率的雪人圖案裁剪成了64個不同的256256的小圖。(256*256是CATiledLayer
的默認(rèn)小圖大小,默認(rèn)大小可以通過tileSize
屬性更改)。程序接受一個圖片路徑作為命令行的第一個參數(shù)。我們可以在編譯的scheme將路徑參數(shù)硬編碼然后就可以在Xcode中運(yùn)行了,但是以后作用在另一個圖片上就不方便了。所以,我們編譯了這個程序并把它保存到敏感的地方,然后從終端調(diào)用,如下面所示:
> path/to/TileCutterApp path/to/Snowman.jpg
The app is very basic, but could easily be extended to support additional arguments such as tile size, or to export images in formats other than JPEG. The result of running it is a sequence of 64 new images, named as follows:
這個程序相當(dāng)基礎(chǔ),但是能夠輕易地擴(kuò)展支持額外的參數(shù)比如小圖大小,或者導(dǎo)出格式等等。運(yùn)行結(jié)果是64個新圖的序列,如下面命名:
Snowman_00_00.jpg
Snowman_00_01.jpg
Snowman_00_02.jpg
...
Snowman_07_07.jpg
既然我們有了裁切后的小圖,我們就要讓iOS程序用到他們。CATiledLayer
很好地和UIScrollView
集成在一起。除了設(shè)置圖層和滑動視圖邊界以適配整個圖片大小,我們真正要做的就是實現(xiàn)-drawLayer:inContext:
方法,當(dāng)需要載入新的小圖時,CATiledLayer
就會調(diào)用到這個方法。
清單6.12演示了代碼。圖6.12是代碼運(yùn)行結(jié)果。
清單6.12 一個簡單的滾動CATiledLayer
實現(xiàn)
#import "ViewController.h"
#import
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIScrollView *scrollView;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//add the tiled layer
CATiledLayer *tileLayer = [CATiledLayer layer];?
tileLayer.frame = CGRectMake(0, 0, 2048, 2048);
tileLayer.delegate = self; [self.scrollView.layer addSublayer:tileLayer];
//configure the scroll view
self.scrollView.contentSize = tileLayer.frame.size;
//draw layer
[tileLayer setNeedsDisplay];
}
- (void)drawLayer:(CATiledLayer *)layer inContext:(CGContextRef)ctx
{
//determine tile coordinate
CGRect bounds = CGContextGetClipBoundingBox(ctx);
NSInteger x = floor(bounds.origin.x / layer.tileSize.width);
NSInteger y = floor(bounds.origin.y / layer.tileSize.height);
//load tile image
NSString *imageName = [NSString stringWithFormat: @"Snowman_%02i_%02i", x, y];
NSString *imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:@"jpg"];
UIImage *tileImage = [UIImage imageWithContentsOfFile:imagePath];
//draw tile
UIGraphicsPushContext(ctx);
[tileImage drawInRect:bounds];
UIGraphicsPopContext();
}
@end
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: