From e04e4b3740afe629536d9929d4867295fe37542d Mon Sep 17 00:00:00 2001 From: Qrox Date: Sat, 21 Dec 2019 22:21:29 +0800 Subject: [PATCH 1/5] Load a list of typeface names --- src/font_loader.h | 11 +++++++++-- src/sdltiles.cpp | 4 +++- src/wincurse.cpp | 3 ++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/font_loader.h b/src/font_loader.h index 67c85da1df525..6e20c9f9f2f28 100644 --- a/src/font_loader.h +++ b/src/font_loader.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "debug.h" #include "filesystem.h" @@ -16,7 +17,7 @@ class font_loader { public: bool fontblending = false; - std::string typeface; + std::vector typeface; std::string map_typeface; std::string overmap_typeface; int fontwidth = 8; @@ -35,7 +36,13 @@ class font_loader std::ifstream stream( path.c_str(), std::ifstream::binary ); JsonIn json( stream ); JsonObject config = json.get_object(); - config.read( "typeface", typeface ); + if( config.has_string( "typeface" ) ) { + std::string tmp; + config.read( "typeface", tmp ); + typeface.emplace_back( tmp ); + } else { + config.read( "typeface", typeface ); + } config.read( "map_typeface", map_typeface ); config.read( "overmap_typeface", overmap_typeface ); } catch( const std::exception &err ) { diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index 7dc3d76ff473e..88f1f3a5ced84 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -3464,7 +3464,9 @@ void catacurses::init_interface() load_soundset(); // Reset the font pointer - font = Font::load_font( fl.typeface, fl.fontsize, fl.fontwidth, fl.fontheight, fl.fontblending ); + assert( !fl.typeface.empty() ); + font = Font::load_font( fl.typeface.front(), fl.fontsize, fl.fontwidth, fl.fontheight, + fl.fontblending ); if( !font ) { throw std::runtime_error( "loading font data failed" ); } diff --git a/src/wincurse.cpp b/src/wincurse.cpp index 09ca2dc1c13d2..4a94e1936b5f4 100644 --- a/src/wincurse.cpp +++ b/src/wincurse.cpp @@ -619,9 +619,10 @@ void catacurses::init_interface() } // Use desired font, if possible + assert( !fl.typeface.empty() ); font = CreateFontW( fontheight, fontwidth, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, - PROOF_QUALITY, FF_MODERN, widen( fl.typeface ).c_str() ); + PROOF_QUALITY, FF_MODERN, widen( fl.typeface.front() ).c_str() ); // Transparent font backgrounds SetBkMode( backbuffer, TRANSPARENT ); From a6aaaeea26fe66e98fcc08867da7720e77b07ee7 Mon Sep 17 00:00:00 2001 From: Qrox Date: Sat, 21 Dec 2019 22:29:33 +0800 Subject: [PATCH 2/5] Change opacity from a member to a parameter --- src/sdltiles.cpp | 53 ++++++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index 88f1f3a5ced84..b168de3af407c 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -109,16 +109,14 @@ class Font { public: Font( int w, int h ) : -#if defined(__ANDROID__) - opacity( 1.0f ), -#endif fontwidth( w ), fontheight( h ) { } virtual ~Font() = default; /** * Draw character t at (x,y) on the screen, * using (curses) color. */ - virtual void OutputChar( const std::string &ch, int x, int y, unsigned char color ) = 0; + virtual void OutputChar( const std::string &ch, int x, int y, + unsigned char color, float opacity = 1.0f ) = 0; virtual void draw_ascii_lines( unsigned char line_id, int drawx, int drawy, int FG ) const; bool draw_window( const catacurses::window &w ); bool draw_window( const catacurses::window &w, int offsetx, int offsety ); @@ -126,9 +124,6 @@ class Font static std::unique_ptr load_font( const std::string &typeface, int fontsize, int fontwidth, int fontheight, bool fontblending ); public: -#if defined(__ANDROID__) - float opacity; // 0-1 -#endif // the width of the font, background is always this size int fontwidth; // the height of the font, background is always this size @@ -144,7 +139,8 @@ class CachedTTFFont : public Font CachedTTFFont( int w, int h, std::string typeface, int fontsize, bool fontblending ); ~CachedTTFFont() override = default; - void OutputChar( const std::string &ch, int x, int y, unsigned char color ) override; + void OutputChar( const std::string &ch, int x, int y, + unsigned char color, float opacity = 1.0f ) override; protected: SDL_Texture_Ptr create_glyph( const std::string &ch, int color ); @@ -181,8 +177,10 @@ class BitmapFont : public Font BitmapFont( int w, int h, const std::string &typeface_path ); ~BitmapFont() override = default; - void OutputChar( const std::string &ch, int x, int y, unsigned char color ) override; - void OutputChar( int t, int x, int y, unsigned char color ); + void OutputChar( const std::string &ch, int x, int y, + unsigned char color, float opacity = 1.0f ) override; + void OutputChar( int t, int x, int y, + unsigned char color, float opacity = 1.0f ); void draw_ascii_lines( unsigned char line_id, int drawx, int drawy, int FG ) const override; protected: std::array::COLOR_NAMES_COUNT> ascii; @@ -657,7 +655,7 @@ SDL_Texture_Ptr CachedTTFFont::create_glyph( const std::string &ch, const int co } void CachedTTFFont::OutputChar( const std::string &ch, const int x, const int y, - const unsigned char color ) + const unsigned char color, const float opacity ) { key_t key {ch, static_cast( color & 0xf )}; @@ -676,26 +674,24 @@ void CachedTTFFont::OutputChar( const std::string &ch, const int x, const int y, return; } SDL_Rect rect {x, y, value.width, fontheight}; -#if defined(__ANDROID__) if( opacity != 1.0f ) { SDL_SetTextureAlphaMod( value.texture.get(), opacity * 255.0f ); } -#endif RenderCopy( renderer, value.texture, nullptr, &rect ); -#if defined(__ANDROID__) if( opacity != 1.0f ) { SDL_SetTextureAlphaMod( value.texture.get(), 255 ); } -#endif } -void BitmapFont::OutputChar( const std::string &ch, int x, int y, unsigned char color ) +void BitmapFont::OutputChar( const std::string &ch, const int x, const int y, + const unsigned char color, const float opacity ) { const int t = UTF8_getch( ch ); - BitmapFont::OutputChar( t, x, y, color ); + BitmapFont::OutputChar( t, x, y, color, opacity ); } -void BitmapFont::OutputChar( int t, int x, int y, unsigned char color ) +void BitmapFont::OutputChar( const int t, const int x, const int y, + const unsigned char color, const float opacity ) { if( t > 256 ) { return; @@ -710,17 +706,13 @@ void BitmapFont::OutputChar( int t, int x, int y, unsigned char color ) rect.y = y; rect.w = fontwidth; rect.h = fontheight; -#if defined(__ANDROID__) if( opacity != 1.0f ) { SDL_SetTextureAlphaMod( ascii[color].get(), opacity * 255 ); } -#endif RenderCopy( renderer, ascii[color], &src, &rect ); -#if defined(__ANDROID__) if( opacity != 1.0f ) { SDL_SetTextureAlphaMod( ascii[color].get(), 255 ); } -#endif } #if defined(__ANDROID__) @@ -2222,10 +2214,10 @@ void draw_quick_shortcuts() text_scale; } text_y = ( WindowHeight - ( height + font->fontheight * text_scale ) * 0.5f ) / text_scale; - font->opacity = get_option( "ANDROID_SHORTCUT_OPACITY_SHADOW" ) * 0.01f; - font->OutputChar( text, text_x + 1, text_y + 1, 0 ); - font->opacity = get_option( "ANDROID_SHORTCUT_OPACITY_FG" ) * 0.01f; - font->OutputChar( text, text_x, text_y, get_option( "ANDROID_SHORTCUT_COLOR" ) ); + font->OutputChar( text, text_x + 1, text_y + 1, 0, + get_option( "ANDROID_SHORTCUT_OPACITY_SHADOW" ) * 0.01f ); + font->OutputChar( text, text_x, text_y, get_option( "ANDROID_SHORTCUT_COLOR" ), + get_option( "ANDROID_SHORTCUT_OPACITY_FG" ) * 0.01f ); if( hovered ) { // draw a second button hovering above the first one font->OutputChar( text, text_x, text_y - ( height * 1.2f / text_scale ), @@ -2242,15 +2234,14 @@ void draw_quick_shortcuts() hint_length ); // scale to fit comfortably } SDL_RenderSetScale( renderer.get(), text_scale, text_scale ); - font->opacity = get_option( "ANDROID_SHORTCUT_OPACITY_SHADOW" ) * 0.01f; text_x = ( WindowWidth - ( ( font->fontwidth * hint_length ) * text_scale ) ) * 0.5f / text_scale; text_y = ( WindowHeight - font->fontheight * text_scale ) * 0.5f / text_scale; - font->OutputChar( hint_text, text_x + 1, text_y + 1, 0 ); - font->opacity = get_option( "ANDROID_SHORTCUT_OPACITY_FG" ) * 0.01f; - font->OutputChar( hint_text, text_x, text_y, get_option( "ANDROID_SHORTCUT_COLOR" ) ); + font->OutputChar( hint_text, text_x + 1, text_y + 1, 0, + get_option( "ANDROID_SHORTCUT_OPACITY_SHADOW" ) * 0.01f ); + font->OutputChar( hint_text, text_x, text_y, get_option( "ANDROID_SHORTCUT_COLOR" ), + get_option( "ANDROID_SHORTCUT_OPACITY_FG" ) * 0.01f ); } } - font->opacity = 1.0f; SDL_RenderSetScale( renderer.get(), 1.0f, 1.0f ); i++; if( ( i + 1 ) * width > WindowWidth ) { From f04d7a4407cac876780434448b88d02f4074b415 Mon Sep 17 00:00:00 2001 From: Qrox Date: Sat, 21 Dec 2019 23:19:12 +0800 Subject: [PATCH 3/5] Function to check if glyph is provided with the font --- src/sdltiles.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index b168de3af407c..792c989b317e1 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -111,6 +111,8 @@ class Font Font( int w, int h ) : fontwidth( w ), fontheight( h ) { } virtual ~Font() = default; + + virtual bool isGlyphProvided( const std::string &ch ) const = 0; /** * Draw character t at (x,y) on the screen, * using (curses) color. @@ -139,6 +141,7 @@ class CachedTTFFont : public Font CachedTTFFont( int w, int h, std::string typeface, int fontsize, bool fontblending ); ~CachedTTFFont() override = default; + bool isGlyphProvided( const std::string &ch ) const; void OutputChar( const std::string &ch, int x, int y, unsigned char color, float opacity = 1.0f ) override; protected: @@ -177,6 +180,7 @@ class BitmapFont : public Font BitmapFont( int w, int h, const std::string &typeface_path ); ~BitmapFont() override = default; + bool isGlyphProvided( const std::string &ch ) const; void OutputChar( const std::string &ch, int x, int y, unsigned char color, float opacity = 1.0f ) override; void OutputChar( int t, int x, int y, @@ -654,6 +658,11 @@ SDL_Texture_Ptr CachedTTFFont::create_glyph( const std::string &ch, const int co return CreateTextureFromSurface( renderer, sglyph ); } +bool CachedTTFFont::isGlyphProvided( const std::string &ch ) const +{ + return TTF_GlyphIsProvided( font.get(), UTF8_getch( ch ) ); +} + void CachedTTFFont::OutputChar( const std::string &ch, const int x, const int y, const unsigned char color, const float opacity ) { @@ -683,6 +692,12 @@ void CachedTTFFont::OutputChar( const std::string &ch, const int x, const int y, } } +bool BitmapFont::isGlyphProvided( const std::string &ch ) const +{ + uint32_t t = UTF8_getch( ch ); + return t < 256; +} + void BitmapFont::OutputChar( const std::string &ch, const int x, const int y, const unsigned char color, const float opacity ) { From 3d3204073c54ff1f441ec66006f55415e61ce6a8 Mon Sep 17 00:00:00 2001 From: Qrox Date: Sun, 22 Dec 2019 00:15:59 +0800 Subject: [PATCH 4/5] Implement font fallback --- src/sdltiles.cpp | 69 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index 792c989b317e1..f5e041b391c48 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -191,7 +191,22 @@ class BitmapFont : public Font int tilewidth; }; -static std::unique_ptr font; +class FontFallbackList : public Font +{ + public: + FontFallbackList( int w, int h, const std::vector &typefaces, + int fontsize, bool fontblending ); + ~FontFallbackList() override = default; + + bool isGlyphProvided( const std::string &ch ) const; + void OutputChar( const std::string &ch, int x, int y, + unsigned char color, float opacity = 1.0f ); + protected: + std::vector> fonts; + std::map>::iterator> glyph_font; +}; + +static std::unique_ptr font; static std::unique_ptr map_font; static std::unique_ptr overmap_font; @@ -3470,12 +3485,8 @@ void catacurses::init_interface() load_soundset(); // Reset the font pointer - assert( !fl.typeface.empty() ); - font = Font::load_font( fl.typeface.front(), fl.fontsize, fl.fontwidth, fl.fontheight, - fl.fontblending ); - if( !font ) { - throw std::runtime_error( "loading font data failed" ); - } + font = std::make_unique( fl.fontwidth, fl.fontheight, + fl.typeface, fl.fontsize, fl.fontblending ); map_font = Font::load_font( fl.map_typeface, fl.map_fontsize, fl.map_fontwidth, fl.map_fontheight, fl.fontblending ); overmap_font = Font::load_font( fl.overmap_typeface, fl.overmap_fontsize, @@ -3832,12 +3843,48 @@ CachedTTFFont::CachedTTFFont( const int w, const int h, std::string typeface, in TTF_SetFontStyle( font.get(), TTF_STYLE_NORMAL ); } +FontFallbackList::FontFallbackList( const int w, const int h, + const std::vector &typefaces, + const int fontsize, const bool fontblending ) + : Font( w, h ) +{ + for( const std::string &typeface : typefaces ) { + std::unique_ptr font = Font::load_font( typeface, fontsize, w, h, fontblending ); + if( !font ) { + throw std::runtime_error( "Cannot load font " + typeface ); + } + fonts.emplace_back( std::move( font ) ); + } + if( fonts.empty() ) { + throw std::runtime_error( "Typeface list is empty" ); + } +} + +bool FontFallbackList::isGlyphProvided( const std::string & ) const +{ + return true; +} + +void FontFallbackList::OutputChar( const std::string &ch, const int x, const int y, + const unsigned char color, const float opacity ) +{ + auto cached = glyph_font.find( ch ); + if( cached == glyph_font.end() ) { + for( auto it = fonts.begin(); it != fonts.end(); ++it ) { + if( std::next( it ) == fonts.end() || ( *it )->isGlyphProvided( ch ) ) { + cached = glyph_font.emplace( ch, it ).first; + } + } + } + ( *cached->second )->OutputChar( ch, x, y, color, opacity ); +} + static int map_font_width() { if( use_tiles && tilecontext ) { return tilecontext->get_tile_width(); } - return ( map_font ? map_font : font )->fontwidth; + return ( map_font ? map_font.get() : font.get() )->fontwidth; } static int map_font_height() @@ -3845,17 +3892,17 @@ static int map_font_height() if( use_tiles && tilecontext ) { return tilecontext->get_tile_height(); } - return ( map_font ? map_font : font )->fontheight; + return ( map_font ? map_font.get() : font.get() )->fontheight; } static int overmap_font_width() { - return ( overmap_font ? overmap_font : font )->fontwidth; + return ( overmap_font ? overmap_font.get() : font.get() )->fontwidth; } static int overmap_font_height() { - return ( overmap_font ? overmap_font : font )->fontheight; + return ( overmap_font ? overmap_font.get() : font.get() )->fontheight; } void to_map_font_dim_width( int &w ) From 5debf8b54bd739f02177cca1e86a42f7ab0c8522 Mon Sep 17 00:00:00 2001 From: Qrox Date: Sun, 22 Dec 2019 10:45:51 +0800 Subject: [PATCH 5/5] Use get_string instead of read, and add override to isGlyphProvided --- src/font_loader.h | 4 +--- src/sdltiles.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/font_loader.h b/src/font_loader.h index 6e20c9f9f2f28..662c297e9b308 100644 --- a/src/font_loader.h +++ b/src/font_loader.h @@ -37,9 +37,7 @@ class font_loader JsonIn json( stream ); JsonObject config = json.get_object(); if( config.has_string( "typeface" ) ) { - std::string tmp; - config.read( "typeface", tmp ); - typeface.emplace_back( tmp ); + typeface.emplace_back( config.get_string( "typeface" ) ); } else { config.read( "typeface", typeface ); } diff --git a/src/sdltiles.cpp b/src/sdltiles.cpp index f5e041b391c48..ee4dd5f1d09f1 100644 --- a/src/sdltiles.cpp +++ b/src/sdltiles.cpp @@ -141,7 +141,7 @@ class CachedTTFFont : public Font CachedTTFFont( int w, int h, std::string typeface, int fontsize, bool fontblending ); ~CachedTTFFont() override = default; - bool isGlyphProvided( const std::string &ch ) const; + bool isGlyphProvided( const std::string &ch ) const override; void OutputChar( const std::string &ch, int x, int y, unsigned char color, float opacity = 1.0f ) override; protected: @@ -180,7 +180,7 @@ class BitmapFont : public Font BitmapFont( int w, int h, const std::string &typeface_path ); ~BitmapFont() override = default; - bool isGlyphProvided( const std::string &ch ) const; + bool isGlyphProvided( const std::string &ch ) const override; void OutputChar( const std::string &ch, int x, int y, unsigned char color, float opacity = 1.0f ) override; void OutputChar( int t, int x, int y, @@ -198,9 +198,9 @@ class FontFallbackList : public Font int fontsize, bool fontblending ); ~FontFallbackList() override = default; - bool isGlyphProvided( const std::string &ch ) const; + bool isGlyphProvided( const std::string &ch ) const override; void OutputChar( const std::string &ch, int x, int y, - unsigned char color, float opacity = 1.0f ); + unsigned char color, float opacity = 1.0f ) override; protected: std::vector> fonts; std::map>::iterator> glyph_font;