OSX use fontAttributes for better emulation of bold fonts (#501)

* using fontAttributes dictionary allows for better emulation of bold typefaces, also switch to using color from context, this allows keeping the same dict throughout, as a side effect fix font caching

* reverting in order to maintain crossplatform compatibility

* applying review suggestions

* applying review suggestions
This commit is contained in:
Stefan Csomor
2017-06-20 17:55:26 +02:00
committed by GitHub
parent e121e8addb
commit 8b1381f2b1
4 changed files with 70 additions and 32 deletions

View File

@@ -2943,6 +2943,7 @@ typedef const void * CFTypeRef;
DECLARE_WXOSX_OPAQUE_CONST_CFREF( CFString ) DECLARE_WXOSX_OPAQUE_CONST_CFREF( CFString )
typedef struct __CFString * CFMutableStringRef; typedef struct __CFString * CFMutableStringRef;
DECLARE_WXOSX_OPAQUE_CONST_CFREF( CFDictionary )
DECLARE_WXOSX_OPAQUE_CFREF( CFRunLoopSource ) DECLARE_WXOSX_OPAQUE_CFREF( CFRunLoopSource )
DECLARE_WXOSX_OPAQUE_CONST_CFREF( CTFont ) DECLARE_WXOSX_OPAQUE_CONST_CFREF( CTFont )

View File

@@ -144,6 +144,7 @@ public:
#endif #endif
CTFontRef OSXGetCTFont() const; CTFontRef OSXGetCTFont() const;
CFDictionaryRef OSXGetCTFontAttributes() const;
#if wxOSX_USE_COCOA #if wxOSX_USE_COCOA
WX_NSFont OSXGetNSFont() const; WX_NSFont OSXGetNSFont() const;

View File

@@ -158,6 +158,7 @@ protected:
public: public:
bool m_fontValid; bool m_fontValid;
wxCFRef<CTFontRef> m_ctFont; wxCFRef<CTFontRef> m_ctFont;
wxCFRef<CFDictionaryRef> m_ctFontAttributes;
wxCFRef<CGFontRef> m_cgFont; wxCFRef<CGFontRef> m_cgFont;
#if wxOSX_USE_COCOA #if wxOSX_USE_COCOA
WX_NSFont m_nsFont; WX_NSFont m_nsFont;
@@ -176,6 +177,7 @@ wxFontRefData::wxFontRefData(const wxFontRefData& data) : wxGDIRefData()
m_info = data.m_info; m_info = data.m_info;
m_fontValid = data.m_fontValid; m_fontValid = data.m_fontValid;
m_ctFont = data.m_ctFont; m_ctFont = data.m_ctFont;
m_ctFontAttributes = data.m_ctFontAttributes;
m_cgFont = data.m_cgFont; m_cgFont = data.m_cgFont;
#if wxOSX_USE_COCOA #if wxOSX_USE_COCOA
m_nsFont = (NSFont*) wxMacCocoaRetain(data.m_nsFont); m_nsFont = (NSFont*) wxMacCocoaRetain(data.m_nsFont);
@@ -271,6 +273,11 @@ wxFontRefData::wxFontRefData(wxOSXSystemFont font, int size)
break; break;
} }
m_ctFont.reset(CTFontCreateUIFontForLanguage( uifont, (CGFloat) size, NULL )); m_ctFont.reset(CTFontCreateUIFontForLanguage( uifont, (CGFloat) size, NULL ));
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
m_ctFontAttributes.reset(dict);
CFDictionarySetValue(dict, kCTFontAttributeName, m_ctFont.get() );
CFDictionarySetValue(dict, kCTForegroundColorFromContextAttributeName, kCFBooleanTrue);
wxCFRef<CTFontDescriptorRef> descr; wxCFRef<CTFontDescriptorRef> descr;
descr.reset( CTFontCopyFontDescriptor( m_ctFont ) ); descr.reset( CTFontCopyFontDescriptor( m_ctFont ) );
m_info.Init(descr); m_info.Init(descr);
@@ -288,6 +295,16 @@ wxFontRefData::wxFontRefData(wxOSXSystemFont font, int size)
static const CGAffineTransform kSlantTransform = CGAffineTransformMake( 1, 0, tan(wxDegToRad(11)), 1, 0, 0 ); static const CGAffineTransform kSlantTransform = CGAffineTransformMake( 1, 0, tan(wxDegToRad(11)), 1, 0, 0 );
namespace
{
struct CachedFontEntry {
wxCFRef< CTFontRef > font;
wxCFRef< CFDictionaryRef > fontAttributes;
} ;
} // anonymous namespace
void wxFontRefData::MacFindFont() void wxFontRefData::MacFindFont()
{ {
if ( m_fontValid ) if ( m_fontValid )
@@ -306,10 +323,19 @@ void wxFontRefData::MacFindFont()
// use font caching // use font caching
wxString lookupnameWithSize = wxString::Format( "%s_%u_%d", m_info.m_faceName, traits, m_info.m_pointSize ); wxString lookupnameWithSize = wxString::Format( "%s_%u_%d", m_info.m_faceName, traits, m_info.m_pointSize );
static std::map< std::wstring , wxCFRef< CTFontRef > > fontcache ; static std::map< wxString, CachedFontEntry > fontcache ;
m_ctFont = fontcache[ std::wstring(lookupnameWithSize.wc_str()) ];
if ( !m_ctFont ) CachedFontEntry& entry = fontcache[ lookupnameWithSize ];
m_ctFont = entry.font;
m_ctFontAttributes = entry.fontAttributes;
if ( m_ctFont )
{ {
// use cached version
}
else
{
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
m_ctFontAttributes.reset(dict);
wxStringToStringHashMap::const_iterator it = gs_FontFamilyToPSName.find(m_info.m_faceName); wxStringToStringHashMap::const_iterator it = gs_FontFamilyToPSName.find(m_info.m_faceName);
@@ -350,11 +376,23 @@ void wxFontRefData::MacFindFont()
fontWithTraits = CTFontCreateCopyWithSymbolicTraits( m_ctFont, 0, remainingTransform, remainingTraits, remainingTraits ); fontWithTraits = CTFontCreateCopyWithSymbolicTraits( m_ctFont, 0, remainingTransform, remainingTraits, remainingTraits );
if ( fontWithTraits == NULL ) if ( fontWithTraits == NULL )
{ {
// give in on the bold, try native oblique // try native oblique, emulate bold later
fontWithTraits = CTFontCreateCopyWithSymbolicTraits( m_ctFont, 0, NULL, kCTFontItalicTrait, kCTFontItalicTrait ); fontWithTraits = CTFontCreateCopyWithSymbolicTraits( m_ctFont, 0, NULL, kCTFontItalicTrait, kCTFontItalicTrait );
} }
else
{
remainingTraits &= ~kCTFontBoldTrait;
}
} }
} }
// we have to emulate bold
if ( remainingTraits & kCTFontBoldTrait )
{
// 3 times as thick, negative value because we want effect on stroke and fill (not only stroke)
const float strokewidth = -3.0;
CFDictionarySetValue(dict, kCTStrokeWidthAttributeName, CFNumberCreate( NULL, kCFNumberFloatType, &strokewidth));
}
if ( fontWithTraits == NULL ) if ( fontWithTraits == NULL )
{ {
@@ -366,6 +404,11 @@ void wxFontRefData::MacFindFont()
m_ctFont.reset(fontWithTraits); m_ctFont.reset(fontWithTraits);
} }
} }
CFDictionarySetValue(dict, kCTFontAttributeName, m_ctFont.get() );
CFDictionarySetValue(dict, kCTForegroundColorFromContextAttributeName, kCFBooleanTrue);
entry.font = m_ctFont;
entry.fontAttributes = m_ctFontAttributes;
} }
m_cgFont.reset(CTFontCopyGraphicsFont(m_ctFont, NULL)); m_cgFont.reset(CTFontCopyGraphicsFont(m_ctFont, NULL));
@@ -378,7 +421,7 @@ void wxFontRefData::MacFindFont()
#endif #endif
m_fontValid = true; m_fontValid = true;
} }
bool wxFontRefData::IsFixedWidth() const bool wxFontRefData::IsFixedWidth() const
{ {
CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(m_ctFont); CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(m_ctFont);
@@ -659,6 +702,16 @@ CTFontRef wxFont::OSXGetCTFont() const
return (CTFontRef)(M_FONTDATA->m_ctFont); return (CTFontRef)(M_FONTDATA->m_ctFont);
} }
CFDictionaryRef wxFont::OSXGetCTFontAttributes() const
{
wxCHECK_MSG( M_FONTDATA != NULL , NULL, wxT("invalid font") );
// cast away constness otherwise lazy font resolution is not possible
const_cast<wxFont *>(this)->RealizeResource();
return (CFDictionaryRef)(M_FONTDATA->m_ctFontAttributes);
}
#if wxOSX_USE_COCOA_OR_CARBON #if wxOSX_USE_COCOA_OR_CARBON
CGFontRef wxFont::OSXGetCGFont() const CGFontRef wxFont::OSXGetCGFont() const

View File

@@ -824,6 +824,7 @@ public:
~wxMacCoreGraphicsFontData(); ~wxMacCoreGraphicsFontData();
CTFontRef OSXGetCTFont() const { return m_ctFont ; } CTFontRef OSXGetCTFont() const { return m_ctFont ; }
CFDictionaryRef OSXGetCTFontAttributes() const { return m_ctFontAttributes; }
wxColour GetColour() const { return m_colour ; } wxColour GetColour() const { return m_colour ; }
bool GetUnderlined() const { return m_underlined ; } bool GetUnderlined() const { return m_underlined ; }
@@ -837,6 +838,7 @@ private :
bool m_underlined, bool m_underlined,
m_strikethrough; m_strikethrough;
wxCFRef< CTFontRef > m_ctFont; wxCFRef< CTFontRef > m_ctFont;
wxCFRef< CFDictionaryRef > m_ctFontAttributes;
#if wxOSX_USE_IPHONE #if wxOSX_USE_IPHONE
UIFont* m_uiFont; UIFont* m_uiFont;
#endif #endif
@@ -849,6 +851,7 @@ wxMacCoreGraphicsFontData::wxMacCoreGraphicsFontData(wxGraphicsRenderer* rendere
m_strikethrough = font.GetStrikethrough(); m_strikethrough = font.GetStrikethrough();
m_ctFont.reset( wxMacCreateCTFont( font ) ); m_ctFont.reset( wxMacCreateCTFont( font ) );
m_ctFontAttributes.reset( wxCFRetain( font.OSXGetCTFontAttributes() ) );
#if wxOSX_USE_IPHONE #if wxOSX_USE_IPHONE
m_uiFont = CreateUIFont(font); m_uiFont = CreateUIFont(font);
wxMacCocoaRetain( m_uiFont ); wxMacCocoaRetain( m_uiFont );
@@ -2220,21 +2223,10 @@ void wxMacCoreGraphicsContext::DoDrawText( const wxString &str, wxDouble x, wxDo
wxMacCoreGraphicsFontData* fref = (wxMacCoreGraphicsFontData*)m_font.GetRefData(); wxMacCoreGraphicsFontData* fref = (wxMacCoreGraphicsFontData*)m_font.GetRefData();
wxCFStringRef text(str, wxLocale::GetSystemEncoding() ); wxCFStringRef text(str, wxLocale::GetSystemEncoding() );
CTFontRef font = fref->OSXGetCTFont();
CGColorRef col = wxMacCreateCGColor( fref->GetColour() ); CGColorRef col = wxMacCreateCGColor( fref->GetColour() );
#if 0 CTFontRef font = fref->OSXGetCTFont();
// right now there's no way to get continuous underlines, only words, so we emulate it
CTUnderlineStyle ustyle = fref->GetUnderlined() ? kCTUnderlineStyleSingle : kCTUnderlineStyleNone ; wxCFRef<CFAttributedStringRef> attrtext( CFAttributedStringCreate(kCFAllocatorDefault, text, fref->OSXGetCTFontAttributes()) );
wxCFRef<CFNumberRef> underlined( CFNumberCreate(NULL, kCFNumberSInt32Type, &ustyle) );
CFStringRef keys[] = { kCTFontAttributeName , kCTForegroundColorAttributeName, kCTUnderlineStyleAttributeName };
CFTypeRef values[] = { font, col, underlined };
#else
CFStringRef keys[] = { kCTFontAttributeName , kCTForegroundColorAttributeName };
CFTypeRef values[] = { font, col };
#endif
wxCFRef<CFDictionaryRef> attributes( CFDictionaryCreate(kCFAllocatorDefault, (const void**) &keys, (const void**) &values,
WXSIZEOF( keys ), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) );
wxCFRef<CFAttributedStringRef> attrtext( CFAttributedStringCreate(kCFAllocatorDefault, text, attributes) );
wxCFRef<CTLineRef> line( CTLineCreateWithAttributedString(attrtext) ); wxCFRef<CTLineRef> line( CTLineCreateWithAttributedString(attrtext) );
y += CTFontGetAscent(font); y += CTFontGetAscent(font);
@@ -2246,6 +2238,7 @@ void wxMacCoreGraphicsContext::DoDrawText( const wxString &str, wxDouble x, wxDo
CGContextScaleCTM(m_cgContext, 1, -1); CGContextScaleCTM(m_cgContext, 1, -1);
CGContextSetTextMatrix(m_cgContext, CGAffineTransformIdentity); CGContextSetTextMatrix(m_cgContext, CGAffineTransformIdentity);
CGContextSetFillColorWithColor( m_cgContext, col );
CTLineDraw( line, m_cgContext ); CTLineDraw( line, m_cgContext );
if ( fref->GetUnderlined() ) { if ( fref->GetUnderlined() ) {
@@ -2316,14 +2309,10 @@ void wxMacCoreGraphicsContext::GetTextExtent( const wxString &str, wxDouble *wid
strToMeasure = wxS(" "); strToMeasure = wxS(" ");
wxMacCoreGraphicsFontData* fref = (wxMacCoreGraphicsFontData*)m_font.GetRefData(); wxMacCoreGraphicsFontData* fref = (wxMacCoreGraphicsFontData*)m_font.GetRefData();
CTFontRef font = fref->OSXGetCTFont();
wxCFStringRef text(strToMeasure, wxLocale::GetSystemEncoding() ); wxCFStringRef text(strToMeasure, wxLocale::GetSystemEncoding() );
CFStringRef keys[] = { kCTFontAttributeName };
CFTypeRef values[] = { font }; wxCFRef<CFAttributedStringRef> attrtext( CFAttributedStringCreate(kCFAllocatorDefault, text, fref->OSXGetCTFontAttributes() ) );
wxCFRef<CFDictionaryRef> attributes( CFDictionaryCreate(kCFAllocatorDefault, (const void**) &keys, (const void**) &values,
WXSIZEOF( keys ), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) );
wxCFRef<CFAttributedStringRef> attrtext( CFAttributedStringCreate(kCFAllocatorDefault, text, attributes) );
wxCFRef<CTLineRef> line( CTLineCreateWithAttributedString(attrtext) ); wxCFRef<CTLineRef> line( CTLineCreateWithAttributedString(attrtext) );
CGFloat a, d, l, w; CGFloat a, d, l, w;
@@ -2336,7 +2325,6 @@ void wxMacCoreGraphicsContext::GetTextExtent( const wxString &str, wxDouble *wid
if ( height ) if ( height )
*height = a+d+l; *height = a+d+l;
} }
if ( descent ) if ( descent )
*descent = d; *descent = d;
if ( externalLeading ) if ( externalLeading )
@@ -2355,14 +2343,9 @@ void wxMacCoreGraphicsContext::GetPartialTextExtents(const wxString& text, wxArr
return; return;
wxMacCoreGraphicsFontData* fref = (wxMacCoreGraphicsFontData*)m_font.GetRefData(); wxMacCoreGraphicsFontData* fref = (wxMacCoreGraphicsFontData*)m_font.GetRefData();
CTFontRef font = fref->OSXGetCTFont();
wxCFStringRef t(text, wxLocale::GetSystemEncoding() ); wxCFStringRef t(text, wxLocale::GetSystemEncoding() );
CFStringRef keys[] = { kCTFontAttributeName }; wxCFRef<CFAttributedStringRef> attrtext( CFAttributedStringCreate(kCFAllocatorDefault, t, fref->OSXGetCTFontAttributes()) );
CFTypeRef values[] = { font };
wxCFRef<CFDictionaryRef> attributes( CFDictionaryCreate(kCFAllocatorDefault, (const void**) &keys, (const void**) &values,
WXSIZEOF( keys ), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) );
wxCFRef<CFAttributedStringRef> attrtext( CFAttributedStringCreate(kCFAllocatorDefault, t, attributes) );
wxCFRef<CTLineRef> line( CTLineCreateWithAttributedString(attrtext) ); wxCFRef<CTLineRef> line( CTLineCreateWithAttributedString(attrtext) );
widths.reserve(text.length()); widths.reserve(text.length());