13.3 臟矩形

2018-02-24 15:07 更新

臟矩形

????有時(shí)候用CAShapeLayer或者其他矢量圖形圖層替代Core Graphics并不是那么切實(shí)可行。比如我們的繪圖應(yīng)用:我們用線條完美地完成了矢量繪制。但是設(shè)想一下如果我們能進(jìn)一步提高應(yīng)用的性能,讓它就像一個(gè)黑板一樣工作,然后用『粉筆』來(lái)繪制線條。模擬粉筆最簡(jiǎn)單的方法就是用一個(gè)『線刷』圖片然后將它粘貼到用戶手指碰觸的地方,但是這個(gè)方法用CAShapeLayer沒(méi)辦法實(shí)現(xiàn)。

????我們可以給每個(gè)『線刷』創(chuàng)建一個(gè)獨(dú)立的圖層,但是實(shí)現(xiàn)起來(lái)有很大的問(wèn)題。屏幕上允許同時(shí)出現(xiàn)圖層上線數(shù)量大約是幾百,那樣我們很快就會(huì)超出的。這種情況下我們沒(méi)什么辦法,就用Core Graphics吧(除非你想用OpenGL做一些更復(fù)雜的事情)。

????我們的『黑板』應(yīng)用的最初實(shí)現(xiàn)見(jiàn)清單13.3,我們更改了之前版本的DrawingView,用一個(gè)畫刷位置的數(shù)組代替UIBezierPath。圖13.2是運(yùn)行結(jié)果

清單13.3 簡(jiǎn)單的類似黑板的應(yīng)用

#import "DrawingView.h"
#import 
#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.3 幀率和線條質(zhì)量會(huì)隨時(shí)間下降。

????為了減少不必要的繪制,Mac OS和iOS設(shè)備將會(huì)把屏幕區(qū)分為需要重繪的區(qū)域和不需要重繪的區(qū)域。那些需要重繪的部分被稱作『臟區(qū)域』。在實(shí)際應(yīng)用中,鑒于非矩形區(qū)域邊界裁剪和混合的復(fù)雜性,通常會(huì)區(qū)分出包含指定視圖的矩形位置,而這個(gè)位置就是『臟矩形』。

????當(dāng)一個(gè)視圖被改動(dòng)過(guò)了,TA可能需要重繪。但是很多情況下,只是這個(gè)視圖的一部分被改變了,所以重繪整個(gè)寄宿圖就太浪費(fèi)了。但是Core Animation通常并不了解你的自定義繪圖代碼,它也不能自己計(jì)算出臟區(qū)域的位置。然而,你的確可以提供這些信息。

????當(dāng)你檢測(cè)到指定視圖或圖層的指定部分需要被重繪,你直接調(diào)用-setNeedsDisplayInRect:來(lái)標(biāo)記它,然后將影響到的矩形作為參數(shù)傳入。這樣就會(huì)在一次視圖刷新時(shí)調(diào)用視圖的-drawRect:(或圖層代理的-drawLayer:inContext:方法)。

????傳入-drawLayer:inContext:CGContext參數(shù)會(huì)自動(dòng)被裁切以適應(yīng)對(duì)應(yīng)的矩形。為了確定矩形的尺寸大小,你可以用CGContextGetClipBoundingBox()方法來(lái)從上下文獲得大小。調(diào)用-drawRect()會(huì)更簡(jiǎn)單,因?yàn)?code>CGRect會(huì)作為參數(shù)直接傳入。

????你應(yīng)該將你的繪制工作限制在這個(gè)矩形中。任何在此區(qū)域之外的繪制都將被自動(dòng)無(wú)視,但是這樣CPU花在計(jì)算和拋棄上的時(shí)間就浪費(fèi)了,實(shí)在是太不值得了。

????相比依賴于Core Graphics為你重繪,裁剪出自己的繪制區(qū)域可能會(huì)讓你避免不必要的操作。那就是說(shuō),如果你的裁剪邏輯相當(dāng)復(fù)雜,那還是讓Core Graphics來(lái)代勞吧,記?。寒?dāng)你能高效完成的時(shí)候才這樣做。

????清單13.4 展示了一個(gè)-addBrushStrokeAtPoint:方法的升級(jí)版,它只重繪當(dāng)前線刷的附近區(qū)域。另外也會(huì)刷新之前線刷的附近區(qū)域,我們也可以用CGRectIntersectsRect()來(lái)避免重繪任何舊的線刷以不至于覆蓋已更新過(guò)的區(qū)域。這樣做會(huì)顯著地提高繪制效率(見(jiàn)圖13.4)

????清單13.4 用-setNeedsDisplayInRect:來(lái)減少不必要的繪制

- (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];
        }
    }
}

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)