[iOS Animation]-CALayer 繪圖效率-髒矩形

髒矩形

有時候用CAShapeLayer或者其餘矢量圖形圖層替代Core Graphics並非那麼切實可行。好比咱們的繪圖應用:咱們用線條完美地完成了矢量繪製。可是設想一下若是咱們能進一步提升應用的性能,讓它就像一個黑板同樣工做,而後用『粉筆』來繪製線條。模擬粉筆最簡單的方法就是用一個『線刷』圖片而後將它粘貼到用戶手指碰觸的地方,可是這個方法用CAShapeLayer沒辦法實現。git

咱們能夠給每一個『線刷』建立一個獨立的圖層,可是實現起來有很大的問題。屏幕上容許同時出現圖層上線數量大約是幾百,那樣咱們很快就會超出的。這種狀況下咱們沒什麼辦法,就用Core Graphics吧(除非你想用OpenGL作一些更復雜的事情)。github

咱們的『黑板』應用的最初實現見清單13.3,咱們更改了以前版本的DrawingView,用一個畫刷位置的數組代替UIBezierPath。圖13.2是運行結果數組

清單13.3 簡單的相似黑板的應用性能

複製代碼

#import "DrawingView.h"#import <QuartzCore/QuartzCore.h>#define BRUSH_SIZE 32@interface DrawingView ()

@property (nonatomic, strong) NSMutableArray *strokes;@end@implementation DrawingView- (void)awakeFromNib
{    //create array
    self.strokes = [NSMutableArray array];
}- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{    //get the starting point
    CGPoint point = [[touches anyObject] locationInView:self];    //add brush stroke    [self addBrushStrokeAtPoint:point];
}- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{    //get the touch point
    CGPoint point = [[touches anyObject] locationInView:self];    //add brush stroke    [self addBrushStrokeAtPoint:point];
}- (void)addBrushStrokeAtPoint:(CGPoint)point
{    //add brush stroke to array    [self.strokes addObject:[NSValue valueWithCGPoint:point]];    //needs redraw    [self setNeedsDisplay];
}- (void)drawRect:(CGRect)rect
{    //redraw strokes
    for (NSValue *value in self.strokes) {        //get point
        CGPoint point = [value CGPointValue];        //get brush rect
        CGRect brushRect = CGRectMake(point.x - BRUSH_SIZE/2, point.y - BRUSH_SIZE/2, BRUSH_SIZE, BRUSH_SIZE);        //draw brush stroke    
        [[UIImage imageNamed:@"Chalk.png"] drawInRect:brushRect];
    }
}@end

複製代碼

 

圖13.2

圖13.2 用程序繪製一個簡單的『素描』atom

這個實如今模擬器上表現還不錯,可是在真實設備上就沒那麼好了。問題在於每次手指移動的時候咱們就會重繪以前的線刷,即便場景的大部分並無改變。咱們繪製地越多,就會越慢。隨着時間的增長每次重繪須要更多的時間,幀數也會降低(見圖13.3),如何提升性能呢?spa

圖13.3

圖13.3 幀率和線條質量會隨時間降低。代理

爲了減小沒必要要的繪製,Mac OS和iOS設備將會把屏幕區分爲須要重繪的區域和不須要重繪的區域。那些須要重繪的部分被稱做『髒區域』。在實際應用中,鑑於非矩形區域邊界裁剪和混合的複雜性,一般會區分出包含指定視圖的矩形位置,而這個位置就是『髒矩形』。code

當一個視圖被改動過了,TA可能須要重繪。可是不少狀況下,只是這個視圖的一部分被改變了,因此重繪整個寄宿圖就太浪費了。可是Core Animation一般並不瞭解你的自定義繪圖代碼,它也不能本身計算出髒區域的位置。然而,你的確能夠提供這些信息。orm

當你檢測到指定視圖或圖層的指定部分須要被重繪,你直接調用 -setNeedsDisplayInRect: 來標記它,而後將影響到的矩形做爲參數傳入。這樣就會在一次視圖刷新時調用視圖的 -drawRect: (或圖層代理的 -drawLayer:inContext: 方法)。圖片

傳入-drawLayer:inContext:CGContext參數會自動被裁切以適應對應的矩形。爲了肯定矩形的尺寸大小,你能夠用CGContextGetClipBoundingBox()方法來從上下文得到大小。調用-drawRect()會更簡單,由於CGRect會做爲參數直接傳入。

你應該將你的繪製工做限制在這個矩形中。任何在此區域以外的繪製都將被自動無視,可是這樣CPU花在計算和拋棄上的時間就浪費了,實在是太不值得了。

相比依賴於Core Graphics爲你重繪,裁剪出本身的繪製區域可能會讓你避免沒必要要的操做。那就是說,若是你的裁剪邏輯至關複雜,那仍是讓Core Graphics來代勞吧,記住:當你能高效完成的時候才這樣作。

清單13.4 展現了一個-addBrushStrokeAtPoint:方法的升級版,它只重繪當前線刷的附近區域。另外也會刷新以前線刷的附近區域,咱們也能夠用CGRectIntersectsRect()來避免重繪任何舊的線刷以不至於覆蓋已更新過的區域。這樣作會顯著地提升繪製效率(見圖13.4)

清單13.4 用-setNeedsDisplayInRect:來減小沒必要要的繪製

複製代碼

- (void)addBrushStrokeAtPoint:(CGPoint)point
{    //add brush stroke to array    [self.strokes addObject:[NSValue valueWithCGPoint:point]];    //set dirty rect    [self setNeedsDisplayInRect:[self brushRectForPoint:point]];
}- (CGRect)brushRectForPoint:(CGPoint)point
{    return CGRectMake(point.x - BRUSH_SIZE/2, point.y - BRUSH_SIZE/2, BRUSH_SIZE, BRUSH_SIZE);
}- (void)drawRect:(CGRect)rect
{    //redraw strokes
    for (NSValue *value in self.strokes) {        //get point
        CGPoint point = [value CGPointValue];        //get brush rect
        CGRect brushRect = [self brushRectForPoint:point];
                //only draw brush stroke if it intersects dirty rect
        if (CGRectIntersectsRect(rect, brushRect)) {            //draw brush stroke
            [[UIImage imageNamed:@"Chalk.png"] drawInRect:brushRect];
        }
    }
}

複製代碼

 

圖13.4

圖13.4 更好的幀率和順滑線條

相關文章
相關標籤/搜索