3
\$\begingroup\$

I'm looking to speed up this method. Ideally, I would like to cut the time in 1/2 or more. At the moment I have to draw the screen line-by-line as the font is 16 pixels high, but it is being drawn with an extra blank pixel on the bottom. If I could figure out how to set the line-height to 16 pixels I would imagine that drawing 1 string over 24 strings would help speed it up. Baring that what else can I do to increase the speed?

- (void)drawRect:(NSRect)dirtyRect
{
    NSGraphicsContext *nsGraphicsContext = [NSGraphicsContext currentContext];
    CGContextRef context = (CGContextRef) [nsGraphicsContext graphicsPort];

    CGContextSetShouldSmoothFonts(context, false);


    NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:@""];
    for (int y=24; y >= 0; y--) {
        [[string mutableString] setString:@""];;
        for (int x=0; x < 80; x++) {
            AnsiScreenChar *c = [self.screen objectAtIndex:(y*80)+x];
            [self.fontAttributes setValue:c.fgColor forKey:NSForegroundColorAttributeName];
            [self.fontAttributes setValue:c.bgColor forKey:NSBackgroundColorAttributeName];

            NSAttributedString *subString = [[NSAttributedString alloc] initWithString:c.data
                                                                            attributes:self.fontAttributes];
            [string appendAttributedString:subString];
        }
        [string drawAtPoint:NSMakePoint(0, self.frame.size.height - ((y+1)*16))];
    }
}

Profile

Running     Time  Self        Symbol Name
3276.0ms    9.5%  3276.0      -[AnsiView drawRect:]
1726.0ms    5.0%  1726.0      -[NSLayoutManager(NSTextViewSupport) showCGGlyphs:positions:count:font:matrix:attributes:inContext:]
1540.0ms    4.4%  1540.0      -[__NSCFDictionary setObject:forKey:]
1467.0ms    4.2%  1467.0      -[NSObject dealloc]
1118.0ms    3.2%  1118.0      -[NSView _drawRect:clip:]
1108.0ms    3.2%  1108.0      -[NSConcreteHashTable getItem:]
1106.0ms    3.2%  1106.0      -[NSLayoutManager(NSPrivate) _drawBackgroundForGlyphRange:atPoint:parameters:]
901.0ms     2.6%  901.0       -[NSLayoutManager(NSPrivate) _insertionPointHelperForGlyphAtIndex:]
779.0ms     2.2%  779.0       -[NSLayoutManager(NSPrivate) _drawGlyphsForGlyphRange:atPoint:]
704.0ms     2.0%  704.0       -[__NSCFString replaceCharactersInRange:withString:]
693.0ms     2.0%  693.0       -[NSATSGlyphStorage createCTTypesetter]
619.0ms     1.8%  619.0       -[NSAttributeDictionary objectForKey:]
599.0ms     1.7%  599.0       -[NSLayoutManager(NSPrivate) _rectArrayForRange:withinSelectionRange:rangeIsCharRange:singleRectOnly:fullLineRectsOnly:inTextContainer:rectCount:rangeWithinContainer:glyphsDrawOutsideLines:]
558.0ms     1.6%  558.0       -[NSLayoutManager locationForGlyphAtIndex:]
537.0ms     1.5%  537.0       +[NSObject allocWithZone:]
\$\endgroup\$
6
\$\begingroup\$

You want to avoid as much work inside drawRect: as possible. Instead of recalculating the attributed string for each line here do it whenever self.screen changes. That way it's done once instead of every time the some portion of the view is redrawn (which will happen a lot).

Maybe keep a self.lines array with the attributed string for each line in it. Or maybe it's a dictionary with the key being the line number and you just keep adding lines and drawRect just draws the last displayHeight lines.

Basically do everything you can to remove unnecessary work from drawRect.

\$\endgroup\$
1
\$\begingroup\$

The code has been changed according to the review and currently looks as follows:

- (void)drawRect:(NSRect)dirtyRect
{
    NSGraphicsContext *nsGraphicsContext = [NSGraphicsContext currentContext];
    CGContextRef context = (CGContextRef) [nsGraphicsContext graphicsPort];
    CGContextSetShouldSmoothFonts(context, false);    

    NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:@""];
    for (int y=24; y >= 0; y--) {
        [[string mutableString] setString:@""];;
        for (int x=0; x < 80; x++) {
            AnsiScreenChar *c = [self.screen objectAtIndex:(y*80)+x];
            [string appendAttributedString:c.data];
        }
        [string drawAtPoint:NSMakePoint(0, self.frame.size.height - ((y+1)*16))];
    }
}

As visible, the inner for-loop has been significantly simplified by removing the fontAttributes changes. Also the superfluous substring-creation has been removed.

This leads to a significantly improved profile, as seen below

Running    Time    Self     Symbol Name
590.0ms    6.5%    590.0        -[AnsiView drawRect:]
561.0ms    6.2%    561.0        -[NSLayoutManager(NSPrivate) _insertionPointHelperForGlyphAtIndex:]
417.0ms    4.6%    417.0        -[NSObject retain]
366.0ms    4.0%    366.0        -[NSLayoutManager(NSPrivate) _drawBackgroundForGlyphRange:atPoint:parameters:]
320.0ms    3.5%    0.0      -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:]
318.0ms    3.5%    318.0        -[NSLayoutManager(NSTextViewSupport) showCGGlyphs:positions:count:font:matrix:attributes:inContext:]
240.0ms    2.6%    240.0        -[NSLayoutManager glyphRangeForCharacterRange:actualCharacterRange:]
236.0ms    2.6%    236.0        -[NSLayoutManager locationForGlyphAtIndex:]
230.0ms    2.5%    230.0        -[__NSCFString replaceCharactersInRange:withString:]
212.0ms    2.3%    0.0      -[NSView _drawRect:clip:]
189.0ms    2.0%    189.0        -[NSATSGlyphStorage createCTTypesetter]
188.0ms    2.0%    188.0        -[NSLayoutManager(NSPrivate) _rectArrayForRange:withinSelectionRange:rangeIsCharRange:singleRectOnly:fullLineRectsOnly:inTextContainer:rectCount:rangeWithinContainer:glyphsDrawOutsideLines:]
183.0ms    2.0%    183.0        -[NSBigMutableString getCharacters:range:]
169.0ms    1.8%    0.0      -[NSFrameView drawWindowBackgroundRect:]
144.0ms    1.5%    144.0        -[NSConcreteGlyphGenerator generateGlyphsForGlyphStorage:desiredNumberOfCharacters:glyphIndex:characterIndex:]
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.