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:]
200_success
145k22 gold badges190 silver badges478 bronze badges
asked Feb 11, 2012 at 8:09
\$\endgroup\$
0

2 Answers 2

5
\$\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.

answered Feb 11, 2012 at 17:32
\$\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

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.