From a663cf8f84942e1147d1d2f3db292308639c7fa5 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Wed, 26 Jan 2011 09:58:29 +0000 Subject: Improved/refactored font functions and (hopefully) sped up cocoa_prepare_layout_manager() svn path=/trunk/netsurf/; revision=11489 --- cocoa/font.m | 222 ++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 135 insertions(+), 87 deletions(-) (limited to 'cocoa/font.m') diff --git a/cocoa/font.m b/cocoa/font.m index 82e8ca594..15debd603 100644 --- a/cocoa/font.m +++ b/cocoa/font.m @@ -31,6 +31,14 @@ static NSLayoutManager *cocoa_prepare_layout_manager( const char *string, size_t length, const plot_font_style_t *style ); + +static CGFloat cocoa_layout_width( NSLayoutManager *layout ); +static CGFloat cocoa_layout_width_chars( NSLayoutManager *layout, size_t characters ); +static NSUInteger cocoa_glyph_for_location( NSLayoutManager *layout, CGFloat x ); +static size_t cocoa_bytes_for_characters( const char *string, size_t characters ); +static NSString *cocoa_string_from_utf8_characters( const char *string, size_t characters ); +static NSDictionary *cocoa_font_attributes( const plot_font_style_t *style ); + static NSTextStorage *cocoa_text_storage = nil; static NSTextContainer *cocoa_text_container = nil; static CGFloat cocoa_font_scale_factor = 1.0; @@ -39,10 +47,8 @@ static bool nsfont_width(const plot_font_style_t *style, const char *string, size_t length, int *width) { - NSCParameterAssert( NULL != width ); - NSLayoutManager *layout = cocoa_prepare_layout_manager( string, length, style ); - *width = NULL != layout ? NSWidth( [layout usedRectForTextContainer: cocoa_text_container] ) : 0; + *width = cocoa_layout_width( layout ); return true; } @@ -51,24 +57,12 @@ static bool nsfont_position_in_string(const plot_font_style_t *style, int x, size_t *char_offset, int *actual_x) { NSLayoutManager *layout = cocoa_prepare_layout_manager( string, length, style ); - CGFloat fraction = 0.0; - NSUInteger glyphIndex = [layout glyphIndexForPoint: NSMakePoint( x, 0 ) - inTextContainer: cocoa_text_container - fractionOfDistanceThroughGlyph: &fraction]; - if (fraction > 0) ++glyphIndex; - NSUInteger chars = [layout characterIndexForGlyphAtIndex: glyphIndex]; + if (layout == nil) return false; - size_t offset = 0; - while (chars-- > 0) { - uint8_t ch = ((uint8_t *)string)[offset]; - - if (0xC2 <= ch && ch <= 0xDF) offset += 2; - else if (0xE0 <= ch && ch <= 0xEF) offset += 3; - else if (0xF0 <= ch && ch <= 0xF4) offset += 4; - else offset++; - } + NSUInteger glyphIndex = cocoa_glyph_for_location( layout, x ); + NSUInteger chars = [layout characterIndexForGlyphAtIndex: glyphIndex]; - *char_offset = offset; + *char_offset = cocoa_bytes_for_characters( string, chars ); *actual_x = [layout locationForGlyphAtIndex: glyphIndex].x; return true; @@ -78,65 +72,97 @@ static bool nsfont_split(const plot_font_style_t *style, const char *string, size_t length, int x, size_t *char_offset, int *actual_x) { - nsfont_position_in_string(style, string, length, x, char_offset, - actual_x); - if (*char_offset == length) return true; + NSLayoutManager *layout = cocoa_prepare_layout_manager( string, length, style ); + if (layout == nil) return false; - while ((string[*char_offset] != ' ') && (*char_offset > 0)) - (*char_offset)--; + NSUInteger glyphIndex = cocoa_glyph_for_location( layout, x ); + NSUInteger chars = [layout characterIndexForGlyphAtIndex: glyphIndex]; + + if (chars == [cocoa_text_storage length] - 1) { + *char_offset = length; + *actual_x = cocoa_layout_width( layout ); + return true; + } - nsfont_position_in_string(style, string, *char_offset, x, char_offset, - actual_x); + chars = [[cocoa_text_storage string] rangeOfString: @" " options: NSBackwardsSearch range: NSMakeRange( 0, chars )].location; + if (chars == NSNotFound) { + *char_offset = 0; + *actual_x = 0; + return true; + } + + *char_offset = cocoa_bytes_for_characters( string, chars ); + *actual_x = cocoa_layout_width_chars( layout, chars ); + return true; } +const struct font_functions nsfont = { + nsfont_width, + nsfont_position_in_string, + nsfont_split +}; + +#pragma mark - -static NSString *cocoa_font_family_name( plot_font_generic_family_t family ) +void cocoa_set_font_scale_factor( float newFactor ) { - switch (family) { - case PLOT_FONT_FAMILY_SERIF: return @"Times"; - case PLOT_FONT_FAMILY_SANS_SERIF: return @"Helvetica"; - case PLOT_FONT_FAMILY_MONOSPACE: return @"Courier"; - case PLOT_FONT_FAMILY_CURSIVE: return @"Apple Chancery"; - case PLOT_FONT_FAMILY_FANTASY: return @"Marker Felt"; - default: return nil; - } + cocoa_font_scale_factor = newFactor; } -static NSFont *cocoa_font_get_nsfont( const plot_font_style_t *style ) +void cocoa_draw_string( int x, int y, const char *bytes, size_t length, const plot_font_style_t *style ) { - NSFont *font = [NSFont fontWithName: cocoa_font_family_name( style->family ) - size: cocoa_font_scale_factor * (CGFloat)style->size / FONT_SIZE_SCALE]; - - NSFontTraitMask traits = 0; - if (style->flags & FONTF_ITALIC || style->flags & FONTF_OBLIQUE) traits |= NSItalicFontMask; - if (style->flags & FONTF_SMALLCAPS) traits |= NSSmallCapsFontMask; - if (style->weight > 400) traits |= NSBoldFontMask; + NSLayoutManager *layout = cocoa_prepare_layout_manager( bytes, length, style ); - if (0 != traits) { - NSFontManager *fm = [NSFontManager sharedFontManager]; - font = [fm convertFont: font toHaveTrait: traits]; + if ([cocoa_text_storage length] > 0) { + NSFont *font = [cocoa_text_storage attribute: NSFontAttributeName atIndex: 0 effectiveRange: NULL]; + CGFloat baseline = [layout defaultBaselineOffsetForFont: font]; + + NSRange glyphRange = [layout glyphRangeForTextContainer: cocoa_text_container]; + [layout drawGlyphsForGlyphRange: glyphRange atPoint: NSMakePoint( x, y - baseline )]; } +} + + +#pragma mark - + +static inline CGFloat cocoa_layout_width( NSLayoutManager *layout ) +{ + if (layout == nil) return 0.0; - return font; + return NSWidth( [layout usedRectForTextContainer: cocoa_text_container] ); } -static NSDictionary *cocoa_font_attributes( const plot_font_style_t *style ) +static inline CGFloat cocoa_layout_width_chars( NSLayoutManager *layout, size_t characters ) { - return [NSDictionary dictionaryWithObjectsAndKeys: - cocoa_font_get_nsfont( style ), NSFontAttributeName, - cocoa_convert_colour( style->foreground ), NSForegroundColorAttributeName, - nil]; + NSUInteger glyphIndex = [layout glyphIndexForCharacterAtIndex: characters]; + return [layout locationForGlyphAtIndex: glyphIndex].x; } -static NSString *cocoa_string_from_utf8_characters( const char *string, size_t characters ) +static inline NSUInteger cocoa_glyph_for_location( NSLayoutManager *layout, CGFloat x ) { - if (NULL == string || 0 == characters) return nil; - - return [[[NSString alloc] initWithBytes: string length:characters encoding:NSUTF8StringEncoding] autorelease]; + CGFloat fraction = 0.0; + NSUInteger glyphIndex = [layout glyphIndexForPoint: NSMakePoint( x, 0 ) + inTextContainer: cocoa_text_container + fractionOfDistanceThroughGlyph: &fraction]; + if (fraction > 0) ++glyphIndex; + return glyphIndex; } +static inline size_t cocoa_bytes_for_characters( const char *string, size_t chars ) +{ + size_t offset = 0; + while (chars-- > 0) { + uint8_t ch = ((uint8_t *)string)[offset]; + + if (0xC2 <= ch && ch <= 0xDF) offset += 2; + else if (0xE0 <= ch && ch <= 0xEF) offset += 3; + else if (0xF0 <= ch && ch <= 0xF4) offset += 4; + else offset++; + } + return offset; +} static NSLayoutManager *cocoa_prepare_layout_manager( const char *bytes, size_t length, const plot_font_style_t *style ) @@ -144,55 +170,77 @@ static NSLayoutManager *cocoa_prepare_layout_manager( const char *bytes, size_t if (NULL == bytes || 0 == length) return nil; static NSLayoutManager *layout = nil; - + if (nil == layout) { cocoa_text_container = [[NSTextContainer alloc] initWithContainerSize: NSMakeSize( CGFLOAT_MAX, CGFLOAT_MAX )]; [cocoa_text_container setLineFragmentPadding: 0]; - + layout = [[NSLayoutManager alloc] init]; [layout addTextContainer: cocoa_text_container]; - - cocoa_text_storage = [[NSTextStorage alloc] init]; - [cocoa_text_storage addLayoutManager: layout]; } - NSString *string = cocoa_string_from_utf8_characters( bytes, length ); - if (nil == string) return nil; + NSString *string = [[[NSString alloc] initWithBytes: bytes length:length encoding:NSUTF8StringEncoding] autorelease]; - NSDictionary *attributes = cocoa_font_attributes( style ); - NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString: string attributes: attributes]; - if (![attributedString isEqualToAttributedString: cocoa_text_storage]) { - [cocoa_text_storage setAttributedString: attributedString]; - [layout ensureLayoutForTextContainer: cocoa_text_container]; + static NSString *oldString = 0; + static plot_font_style_t oldStyle = { 0 }; + + const bool styleChanged = memcmp( style, &oldStyle, sizeof oldStyle ) != 0; + + if ([oldString isEqualToString: string] && !styleChanged) { + return layout; } - [attributedString release]; + + [oldString release]; + oldString = [string copy]; + oldStyle = *style; + + static NSDictionary *attributes = nil; + if (styleChanged || attributes == nil) { + [attributes release]; + attributes = [cocoa_font_attributes( style ) retain]; + } + + [cocoa_text_storage release]; + cocoa_text_storage = [[NSTextStorage alloc] initWithString: string attributes: attributes]; + [cocoa_text_storage addLayoutManager: layout]; + + [layout ensureLayoutForTextContainer: cocoa_text_container]; return layout; } -void cocoa_set_font_scale_factor( float newFactor ) -{ - cocoa_font_scale_factor = newFactor; -} +static NSString * const cocoa_font_families[PLOT_FONT_FAMILY_COUNT] = { + [PLOT_FONT_FAMILY_SERIF] = @"Times", + [PLOT_FONT_FAMILY_SANS_SERIF] = @"Helvetica", + [PLOT_FONT_FAMILY_MONOSPACE] = @"Courier", + [PLOT_FONT_FAMILY_CURSIVE] = @"Apple Chancery", + [PLOT_FONT_FAMILY_FANTASY] = @"Marker Felt" +}; -void cocoa_draw_string( int x, int y, const char *bytes, size_t length, const plot_font_style_t *style ) +static inline NSFont *cocoa_font_get_nsfont( const plot_font_style_t *style ) { - NSLayoutManager *layout = cocoa_prepare_layout_manager( bytes, length, style ); + NSFont *font = [NSFont fontWithName: cocoa_font_families[style->family] + size: cocoa_font_scale_factor * (CGFloat)style->size / FONT_SIZE_SCALE]; - if ([cocoa_text_storage length] > 0) { - NSFont *font = [cocoa_text_storage attribute: NSFontAttributeName atIndex: 0 effectiveRange: NULL]; - CGFloat baseline = [layout defaultBaselineOffsetForFont: font]; - - NSRange glyphRange = [layout glyphRangeForTextContainer: cocoa_text_container]; - [layout drawGlyphsForGlyphRange: glyphRange atPoint: NSMakePoint( x, y - baseline )]; + NSFontTraitMask traits = 0; + if (style->flags & FONTF_ITALIC || style->flags & FONTF_OBLIQUE) traits |= NSItalicFontMask; + if (style->flags & FONTF_SMALLCAPS) traits |= NSSmallCapsFontMask; + if (style->weight > 400) traits |= NSBoldFontMask; + + if (0 != traits) { + NSFontManager *fm = [NSFontManager sharedFontManager]; + font = [fm convertFont: font toHaveTrait: traits]; } + + return font; } -const struct font_functions nsfont = { - nsfont_width, - nsfont_position_in_string, - nsfont_split -}; - +static inline NSDictionary *cocoa_font_attributes( const plot_font_style_t *style ) +{ + return [NSDictionary dictionaryWithObjectsAndKeys: + cocoa_font_get_nsfont( style ), NSFontAttributeName, + cocoa_convert_colour( style->foreground ), NSForegroundColorAttributeName, + nil]; +} \ No newline at end of file -- cgit v1.2.3