Remove SDL_ttf wrapper API

This removes the build-time dependencies on SDL_ttf and FriBidi,
alongside the SDL_ttf wrappers, the SDL_ttf text surface class, the
SDL_ttf render cache, and the SDL_ttf (de)initialization code.
This commit is contained in:
Iris Morelle 2021-03-10 16:39:29 -03:00
parent 8a9e861eb3
commit 2dfdc0061d
22 changed files with 6 additions and 1146 deletions

View file

@ -40,9 +40,6 @@ set(LOCALEDIR "translations" CACHE STRING "change the name of the locale data di
set(PREFERENCES_DIR "" CACHE STRING "Use a non-default preferences directory (.wesnoth on unix)") set(PREFERENCES_DIR "" CACHE STRING "Use a non-default preferences directory (.wesnoth on unix)")
set(DEFAULT_PREFS_FILE "" CACHE STRING "Set system wide preferences file") set(DEFAULT_PREFS_FILE "" CACHE STRING "Set system wide preferences file")
#Game options
option(ENABLE_FRIBIDI "Enable FriBIDi support" ON)
#server options #server options
set(SERVER_UID "" CACHE STRING "User id of the user who runs wesnothd") set(SERVER_UID "" CACHE STRING "User id of the user who runs wesnothd")
set(SERVER_GID "" CACHE STRING "Group id of the user who runs wesnothd") set(SERVER_GID "" CACHE STRING "Group id of the user who runs wesnothd")
@ -521,7 +518,6 @@ if(ENABLE_GAME OR ENABLE_TESTS)
find_package(SDL2 2.0.4 REQUIRED) find_package(SDL2 2.0.4 REQUIRED)
find_package(SDL2_image 2.0.2 REQUIRED) find_package(SDL2_image 2.0.2 REQUIRED)
find_package(SDL2_mixer 2.0.0 REQUIRED) find_package(SDL2_mixer 2.0.0 REQUIRED)
find_package(SDL2_ttf 2.0.12 REQUIRED)
find_package(VorbisFile REQUIRED) find_package(VorbisFile REQUIRED)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(CAIRO REQUIRED cairo>=1.10) pkg_check_modules(CAIRO REQUIRED cairo>=1.10)
@ -535,15 +531,6 @@ if(ENABLE_TESTS)
endif(ENABLE_TESTS) endif(ENABLE_TESTS)
if(ENABLE_GAME) if(ENABLE_GAME)
if(ENABLE_FRIBIDI)
PKG_CHECK_MODULES(FRIBIDI fribidi>=0.10.9)
if(FRIBIDI_FOUND)
add_definitions(-DHAVE_FRIBIDI)
elseif(NOT FRIBIDI_FOUND)
message("Could not find FriBiDi. Disabling FriBiDi support.")
endif(FRIBIDI_FOUND)
endif(ENABLE_FRIBIDI)
if(ENABLE_NOTIFICATIONS) if(ENABLE_NOTIFICATIONS)
pkg_check_modules(LIBDBUS dbus-1) pkg_check_modules(LIBDBUS dbus-1)
if(LIBDBUS_FOUND) if(LIBDBUS_FOUND)

View file

@ -65,7 +65,6 @@ opts.AddVariables(
('cachedir', 'Directory that contains a cache of derived files.', ''), ('cachedir', 'Directory that contains a cache of derived files.', ''),
PathVariable('datadir', 'read-only architecture-independent game data', "$datarootdir/$datadirname", PathVariable.PathAccept), PathVariable('datadir', 'read-only architecture-independent game data', "$datarootdir/$datadirname", PathVariable.PathAccept),
PathVariable('fifodir', 'directory for the wesnothd fifo socket file', "/var/run/wesnothd", PathVariable.PathAccept), PathVariable('fifodir', 'directory for the wesnothd fifo socket file', "/var/run/wesnothd", PathVariable.PathAccept),
BoolVariable('fribidi','Clear to disable bidirectional-language support', True),
BoolVariable('desktop_entry','Clear to disable desktop-entry', True), BoolVariable('desktop_entry','Clear to disable desktop-entry', True),
BoolVariable('appdata_file','Clear to not install appdata file', True), BoolVariable('appdata_file','Clear to not install appdata file', True),
BoolVariable('systemd','Install systemd unit file for wesnothd', bool(WhereIs("systemd"))), BoolVariable('systemd','Install systemd unit file for wesnothd', bool(WhereIs("systemd"))),
@ -363,7 +362,6 @@ if env["prereqs"]:
def have_sdl_other(): def have_sdl_other():
return \ return \
conf.CheckSDL(require_version = '2.0.4') & \ conf.CheckSDL(require_version = '2.0.4') & \
conf.CheckSDL("SDL2_ttf", header_file = "SDL_ttf") & \
conf.CheckSDL("SDL2_mixer", header_file = "SDL_mixer") & \ conf.CheckSDL("SDL2_mixer", header_file = "SDL_mixer") & \
conf.CheckSDL("SDL2_image", header_file = "SDL_image") conf.CheckSDL("SDL2_image", header_file = "SDL_image")
@ -418,7 +416,6 @@ if env["prereqs"]:
have_X = conf.CheckLib('X11') have_X = conf.CheckLib('X11')
env["notifications"] = env["notifications"] and conf.CheckPKG("dbus-1") env["notifications"] = env["notifications"] and conf.CheckPKG("dbus-1")
client_env['fribidi'] = client_env['fribidi'] and (conf.CheckPKG('fribidi >= 0.10.9') or Warning("Can't find FriBiDi, disabling FriBiDi support."))
env["history"] = env["history"] and (conf.CheckLib("history") or Warning("Can't find GNU history, disabling history support.")) env["history"] = env["history"] and (conf.CheckLib("history") or Warning("Can't find GNU history, disabling history support."))
client_env = conf.Finish() client_env = conf.Finish()
@ -426,8 +423,6 @@ if env["prereqs"]:
# We set those outside of Configure() section because SCons doesn't merge CPPPATH var properly in conf.Finish() # We set those outside of Configure() section because SCons doesn't merge CPPPATH var properly in conf.Finish()
if env["notifications"]: if env["notifications"]:
client_env.Append(CPPDEFINES = ["HAVE_LIBDBUS"]) client_env.Append(CPPDEFINES = ["HAVE_LIBDBUS"])
if client_env['fribidi']:
client_env.Append(CPPDEFINES = ["HAVE_FRIBIDI"])
if env["history"]: if env["history"]:
client_env.Append(CPPDEFINES = ["HAVE_HISTORY"]) client_env.Append(CPPDEFINES = ["HAVE_HISTORY"])

View file

@ -8,13 +8,10 @@ events.cpp
floating_label.cpp floating_label.cpp
font/font_config.cpp font/font_config.cpp
font/marked-up_text.cpp font/marked-up_text.cpp
font/sdl_ttf.cpp
font/sdl_ttf_compat.cpp font/sdl_ttf_compat.cpp
font/standard_colors.cpp font/standard_colors.cpp
font/text.cpp font/text.cpp
font/text_cache.cpp
font/text_formatting.cpp font/text_formatting.cpp
font/text_surface.cpp
format_time_summary.cpp format_time_summary.cpp
formula/string_utils.cpp formula/string_utils.cpp
game_end_exceptions.cpp game_end_exceptions.cpp

View file

@ -32,10 +32,6 @@ if(SDL2MIXER_INCLUDE_DIR)
include_directories(SYSTEM ${SDL2MIXER_INCLUDE_DIR} ) include_directories(SYSTEM ${SDL2MIXER_INCLUDE_DIR} )
set(sdl_mixer-lib ${SDL2_MIXER_LIBRARY}) set(sdl_mixer-lib ${SDL2_MIXER_LIBRARY})
endif() endif()
if(SDL2TTF_INCLUDE_DIR)
include_directories(SYSTEM ${SDL2TTF_INCLUDE_DIR} )
set(sdl_ttf-lib ${SDL2_TTF_LIBRARY})
endif()
if(ZLIB_INCLUDE_DIR) if(ZLIB_INCLUDE_DIR)
include_directories(SYSTEM ${ZLIB_INCLUDE_DIR} ) include_directories(SYSTEM ${ZLIB_INCLUDE_DIR} )
@ -77,7 +73,6 @@ set(game-external-libs
${Boost_THREAD_LIBRARY} ${Boost_THREAD_LIBRARY}
${sdl_image-lib} ${sdl_image-lib}
${sdl_mixer-lib} ${sdl_mixer-lib}
${sdl_ttf-lib}
${PANGOCAIRO_LIBRARIES} ${PANGOCAIRO_LIBRARIES}
${FONTCONFIG_LIBRARIES} ${FONTCONFIG_LIBRARIES}
${LIBDBUS_LIBRARIES} ${LIBDBUS_LIBRARIES}
@ -103,11 +98,6 @@ set(server-external-libs
-lpthread -lpthread
) )
if(ENABLE_FRIBIDI AND FRIBIDI_FOUND)
set(game-external-libs ${game-external-libs} ${FRIBIDI_LIBRARIES})
include_directories(SYSTEM ${FRIBIDI_INCLUDE_DIRS} )
endif(ENABLE_FRIBIDI AND FRIBIDI_FOUND)
if(APPLE) if(APPLE)
set(game-external-libs ${game-external-libs} "-framework IOKit") set(game-external-libs ${game-external-libs} "-framework IOKit")
endif(APPLE) endif(APPLE)

View file

@ -21,7 +21,6 @@
#include "cursor.hpp" #include "cursor.hpp"
#include "display.hpp" #include "display.hpp"
#include "fake_unit_manager.hpp" #include "fake_unit_manager.hpp"
#include "font/sdl_ttf.hpp"
#include "font/sdl_ttf_compat.hpp" #include "font/sdl_ttf_compat.hpp"
#include "font/text.hpp" #include "font/text.hpp"
#include "preferences/game.hpp" #include "preferences/game.hpp"

View file

@ -15,7 +15,6 @@
#include "font/font_config.hpp" #include "font/font_config.hpp"
#include "font/font_description.hpp" #include "font/font_description.hpp"
#include "font/error.hpp" #include "font/error.hpp"
#include "font/sdl_ttf.hpp"
#include "config.hpp" #include "config.hpp"
#include "log.hpp" #include "log.hpp"
@ -192,7 +191,6 @@ bool load_font_config()
if(fontlist.empty()) if(fontlist.empty())
return false; return false;
sdl_ttf::set_font_list(fontlist);
return true; return true;
} }

View file

@ -16,12 +16,10 @@
/*** /***
* The font::manager initializes cairo and font_config in order to figure out * The font::manager initializes cairo and font_config in order to figure out
* what local fonts to use. It also asks SDL_TTF to initialize itself, via the * what local fonts to use.
* sdl_ttf raii object.
*/ */
#include "font_options.hpp" #include "font_options.hpp"
#include "sdl_ttf.hpp"
class t_string; class t_string;
@ -41,11 +39,6 @@ struct manager {
manager(const manager &) = delete; manager(const manager &) = delete;
manager & operator = (const manager &) = delete; manager & operator = (const manager &) = delete;
private:
/** Initialize sdl_ttf concurrent with font::manager lifetime */
sdl_ttf sdl_ttf_initializer_;
}; };
/*** /***

View file

@ -1,86 +0,0 @@
/*
Copyright (C) 2016 - 2018 by Chris Beck<render787@gmail.com>
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/***
* Note: Specific to SDL_TTF code path
*/
#pragma once
#include <string>
#include <tuple>
#include <SDL2/SDL_ttf.h>
/***
* Note: This is specific to SDL_TTF code path
*/
namespace font
{
/**
* Font family, acts an an enumeration with each font loaded by stl_ttf::set_font_list getting an
* individual value. The values do not necessarily correspond to the order of the list passed to
* stl_ttf::set_font_list, all positive values should be treated as opaque data.
*
* Negative values are returned by sdl_ttf::split_text to denote chunks which can't be handled with
* the available fonts.
*/
typedef int subset_id;
// Used as a key in requests to the functions in sdl_text.hpp (and the font table in sdl_text.cpp's implementation)
struct font_id
{
explicit font_id(subset_id subset, int size) : subset(subset), size(size), style(TTF_STYLE_NORMAL) {}
explicit font_id(subset_id subset, int size, int style) : subset(subset), size(size), style(style) {}
bool operator==(const font_id& o) const
{
return subset == o.subset && size == o.size && style == o.style;
}
bool operator<(const font_id& o) const
{
return std::tie(subset, size, style) < std::tie(o.subset, o.size, o.style);
}
subset_id subset;
int size;
/**
* Bitmask of the values TTF_STYLE_BOLD, TTF_STYLE_ITALIC.
*/
int style;
};
/**
* A string that should be rendered with a single font. Longer texts that need
* characters from multiple fonts are cut into these sub-strings.
*
* Text chunk is used by text_surfaces and these are cached sometimes.
*/
struct text_chunk
{
text_chunk(subset_id subset, std::string&& text)
: subset(subset)
, text(std::move(text))
{
}
bool operator==(const text_chunk& t) const { return subset == t.subset && text == t.text; }
bool operator!=(const text_chunk& t) const { return !operator==(t); }
subset_id subset;
std::string text;
};
} // end namespace font

View file

@ -1,566 +0,0 @@
/*
Copyright (C) 2016 - 2018 by Chris Beck<render787@gmail.com>
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#include "font/sdl_ttf.hpp"
#include "font/error.hpp"
#include "font/font_config.hpp"
#include "font/font_id.hpp"
#include "font/text_cache.hpp"
#include "font/text_surface.hpp"
#include "filesystem.hpp"
#include "font/marked-up_text.hpp"
#include "game_config.hpp"
#include "log.hpp"
#include "preferences/general.hpp"
#include "tooltips.hpp"
#include "sdl/rect.hpp"
#include "sdl/surface.hpp"
#include "serialization/unicode.hpp"
#include <SDL2/SDL_ttf.h>
#include <map>
#include <string>
#include <vector>
static lg::log_domain log_font("font");
#define DBG_FT LOG_STREAM(debug, log_font)
#define LOG_FT LOG_STREAM(info, log_font)
#define WRN_FT LOG_STREAM(warn, log_font)
#define ERR_FT LOG_STREAM(err, log_font)
namespace font
{
namespace
{
// Record stored in the font table.
// If the record for font_id (FOO, Bold + Underline) is a record (BAR, Bold),
// it means that BAR is a Bold-styled version of FOO which we shipped with the
// game, and now SDL_TTF should be used to style BAR as underline for the final results.
struct ttf_record
{
std::shared_ptr<TTF_Font> font;
int style;
};
static std::map<font_id, ttf_record> font_table;
// The indices in these vectors correspond to the font_id.subset values in font_table.
static std::vector<std::string> font_names;
static std::vector<std::string> bold_names;
static std::vector<std::string> italic_names;
struct family_record
{
std::shared_ptr<const TTF_Font> font;
subset_id subset;
std::string name;
};
/**
* Used for implementing find_font_containing, the elements are in the same order as the arguments
* to set_font_list(). The fonts here are a subset of those in font_table, because
* find_font_containing doesn't need size-specific instances of a font.
*
* In most locales, the subset_ids will match the indices into this vector. This is only a
* coincidence, and it won't be true (at the time of writing) in Chinese.
*
* \todo Are all variants of a font guaranteed to have exactly the same glyphs? For example, might
* an italic variant only contain the glyphs which are major improvements on an automatic skew of
* the non-italic version?
*/
std::vector<family_record> family_table;
const auto no_font_found = family_record{nullptr, -1, ""};
/**
* Given a unicode code point, returns the first (using the order passed to set_font_list) font
* that includes that code point. Returns no_font_found if none of the known fonts contain this value.
*/
const family_record& find_font_containing(int ch)
{
for(const auto& i : family_table) {
if(TTF_GlyphIsProvided(i.font.get(), ch)) {
return i;
}
}
LOG_FT << "Glyph " << ch << " not provided by any font\n";
return no_font_found;
}
// cache sizes of small text
typedef std::map<std::string, SDL_Rect> line_size_cache_map;
// map of styles -> sizes -> cache
static std::map<int, std::map<int, line_size_cache_map>> line_size_cache;
/**
* Destructor for using std::unique_ptr or std::shared_ptr as an RAII holder for a TTF_Font.
*/
struct font_deleter
{
void operator()(TTF_Font* font)
{
if(font != nullptr)
TTF_CloseFont(font);
}
};
std::shared_ptr<TTF_Font> open_font(const std::string& fname, int size)
{
std::string name;
if(!game_config::path.empty()) {
name = game_config::path + "/fonts/" + fname;
if(!filesystem::file_exists(name)) {
name = "fonts/" + fname;
if(!filesystem::file_exists(name)) {
name = fname;
if(!filesystem::file_exists(name)) {
ERR_FT << "Failed opening font: '" << name << "': No such file or directory" << std::endl;
return nullptr;
}
}
}
} else {
name = "fonts/" + fname;
if(!filesystem::file_exists(name)) {
if(!filesystem::file_exists(fname)) {
ERR_FT << "Failed opening font: '" << name << "': No such file or directory" << std::endl;
return nullptr;
}
name = fname;
}
}
filesystem::rwops_ptr rwops = filesystem::make_read_RWops(name);
std::unique_ptr<TTF_Font, font_deleter> font;
font.reset(TTF_OpenFontRW(rwops.release(), true, size)); // SDL takes ownership of rwops
if(font == nullptr) {
ERR_FT << "Failed opening font: '" << fname << "'\n";
ERR_FT << "TTF_OpenFont: " << TTF_GetError() << std::endl;
return nullptr;
}
DBG_FT << "Opened a font: " << fname << ", in size " << size << std::endl;
return font;
}
} // anonymous namespace
// Gets an appropriately configured TTF Font, for this font size and style.
// Loads fonts if necessary. For styled fonts, we search for a ``shipped''
// version of the font which is prestyled. If this fails we find the closest
// thing which we did ship, and store a record of this, which allows to
// rapidly correct the remaining styling using SDL_TTF.
//
// Uses the font table for caching.
std::shared_ptr<TTF_Font> sdl_ttf::get_font(font_id id)
{
const auto it = font_table.find(id);
if(it != font_table.end() && it->second.font != nullptr) {
return it->second.font;
}
// There's no record, so we need to try to find a solution for this font
// and make a record of it. If the indices are out of bounds don't bother though.
if(id.subset < 0 || std::size_t(id.subset) >= font_names.size()) {
return nullptr;
}
// Favor to use the shipped Italic font over bold if both are present and are needed.
if((id.style & TTF_STYLE_ITALIC) && italic_names[id.subset].size()) {
if(auto font = open_font(italic_names[id.subset], id.size)) {
ttf_record rec{font, TTF_STYLE_ITALIC};
// The next line adds bold if needed
TTF_SetFontStyle(font.get(), id.style ^ TTF_STYLE_ITALIC);
font_table.emplace(id, rec);
return font;
}
}
// Now see if the shipped Bold font is useful and available.
if((id.style & TTF_STYLE_BOLD) && bold_names[id.subset].size()) {
if(auto font = open_font(bold_names[id.subset], id.size)) {
ttf_record rec{font, TTF_STYLE_BOLD};
// The next line adds italic if needed
TTF_SetFontStyle(font.get(), id.style ^ TTF_STYLE_BOLD);
font_table.emplace(id, rec);
return font;
}
}
// Try just to use the basic version of the font then.
if(font_names[id.subset].size()) {
if(auto font = open_font(font_names[id.subset], id.size)) {
ttf_record rec{font, TTF_STYLE_NORMAL};
TTF_SetFontStyle(font.get(), id.style);
font_table.emplace(id, rec);
return font;
}
}
// Failed to find a font.
ttf_record rec{nullptr, TTF_STYLE_NORMAL};
font_table.emplace(id, rec);
return nullptr;
}
/***
* Interface to SDL_TTF
*/
static surface render_text(const std::string& text, int fontsize, const color_t& color, int style)
{
// we keep blank lines and spaces (may be wanted for indentation)
const std::vector<std::string> lines = utils::split(text, '\n', 0);
std::vector<std::vector<surface>> surfaces;
surfaces.reserve(lines.size());
std::size_t width = 0, height = 0;
for(std::vector< std::string >::const_iterator ln = lines.begin(), ln_end = lines.end(); ln != ln_end; ++ln) {
int sz = fontsize;
int text_style = style;
text_surface txt_surf(sz, color, text_style);
txt_surf.set_text(*ln);
const text_surface& cached_surf = text_cache::find(txt_surf);
const std::vector<surface>&res = cached_surf.get_surfaces();
if (!res.empty()) {
surfaces.push_back(res);
width = std::max<std::size_t>(cached_surf.width(), width);
height += cached_surf.height();
}
}
if (surfaces.empty()) {
return surface();
} else if (surfaces.size() == 1 && surfaces.front().size() == 1) {
surface surf = surfaces.front().front();
return surf;
} else {
surface res(width,height);
if (!res)
return res;
std::size_t ypos = 0;
for(std::vector< std::vector<surface>>::iterator i = surfaces.begin(),
i_end = surfaces.end(); i != i_end; ++i) {
std::size_t xpos = 0;
height = 0;
for(std::vector<surface>::iterator j = i->begin(),
j_end = i->end(); j != j_end; ++j) {
SDL_Rect dstrect = sdl::create_rect(xpos, ypos, 0, 0);
blit_surface(*j, nullptr, res, &dstrect);
xpos += (*j)->w;
height = std::max<std::size_t>((*j)->h, height);
}
ypos += height;
}
return res;
}
}
surface get_rendered_text(const std::string& str, int size, const color_t& color, int style)
{
return render_text(str, size, color, style);
}
SDL_Rect draw_text_line(surface& gui_surface, const SDL_Rect& area, int size,
const color_t& color, const std::string& text,
int x, int y, bool use_tooltips, int style)
{
size = preferences::font_scaled(size);
if (!gui_surface) {
const text_surface& u = text_cache::find(text_surface(text, size, color, style));
return sdl::create_rect(0, 0, u.width(), u.height());
}
if(area.w == 0) { // no place to draw
return {0, 0, 0, 0};
}
const std::string etext = make_text_ellipsis(text, size, area.w);
surface surface(render_text(etext,size,color,style));
if(surface == nullptr) {
return {0, 0, 0, 0};
}
SDL_Rect dest;
if(x!=-1) {
dest.x = x;
#ifdef HAVE_FRIBIDI
// Oron -- Conditional, until all draw_text_line calls have fixed area parameter
if(getenv("NO_RTL") == nullptr) {
bool is_rtl = text_cache::find(text_surface(text, size, color, style)).is_rtl();
if(is_rtl)
dest.x = area.x + area.w - surface->w - (x - area.x);
}
#endif
} else
dest.x = (area.w/2)-(surface->w/2);
if(y!=-1)
dest.y = y;
else
dest.y = (area.h/2)-(surface->h/2);
dest.w = surface->w;
dest.h = surface->h;
if(line_width(text, size) > area.w) {
tooltips::add_tooltip(dest,text);
}
if(dest.x + dest.w > area.x + area.w) {
dest.w = area.x + area.w - dest.x;
}
if(dest.y + dest.h > area.y + area.h) {
dest.h = area.y + area.h - dest.y;
}
if(gui_surface != nullptr) {
SDL_Rect src = dest;
src.x = 0;
src.y = 0;
sdl_blit(surface,&src,gui_surface,&dest);
}
if(use_tooltips) {
tooltips::add_tooltip(dest,text);
}
return dest;
}
int line_width(const std::string& line, int font_size, int style)
{
return line_size(line,font_size,style).w;
}
SDL_Rect line_size(const std::string& line, int font_size, int style)
{
line_size_cache_map& cache = line_size_cache[style][font_size];
const line_size_cache_map::const_iterator i = cache.find(line);
if(i != cache.end()) {
return i->second;
}
SDL_Rect res;
const color_t col { 0, 0, 0, 0 };
text_surface s(line, font_size, col, style);
res.w = s.width();
res.h = s.height();
res.x = res.y = 0;
cache.emplace(line,res);
return res;
}
std::string make_text_ellipsis(const std::string &text, int font_size,
int max_width, int style)
{
if (line_width(text, font_size, style) <= max_width)
return text;
if(line_width(ellipsis, font_size, style) > max_width)
return "";
std::string current_substring;
try {
utf8::iterator itor(text);
for(; itor != utf8::iterator::end(text); ++itor) {
std::string tmp = current_substring;
tmp.append(itor.substr().first, itor.substr().second);
if (line_width(tmp + ellipsis, font_size, style) > max_width) {
return current_substring + ellipsis;
}
current_substring.append(itor.substr().first, itor.substr().second);
}
}
catch(utf8::invalid_utf8_exception&) {
WRN_FT << "Invalid UTF-8 string: \"" << text << "\"" << std::endl;
return "";
}
return text; // Should not happen
}
/***
* Initialize and destruction
*/
sdl_ttf::sdl_ttf()
{
const int res = TTF_Init();
if(res == -1) {
ERR_FT << "Could not initialize SDL_TTF" << std::endl;
throw font::error("SDL_TTF could not initialize, TTF_INIT returned: " + std::to_string(res));
} else {
LOG_FT << "Initialized true type fonts\n";
}
}
static void clear_fonts()
{
// Ensure that the shared_ptr<TTF_Font>s' destructors run before TTF_Quit().
font_table.clear();
family_table.clear();
font_names.clear();
bold_names.clear();
italic_names.clear();
line_size_cache.clear();
}
sdl_ttf::~sdl_ttf()
{
clear_fonts();
TTF_Quit();
}
// sets the font list to be used.
void sdl_ttf::set_font_list(const std::vector<subset_descriptor>& fontlist)
{
// Wesnoth's startup sequence usually loads the same set of fonts twice.
// See if we can use the already-loaded fonts.
if(!font_names.empty()) {
std::vector<family_record> reordered_family_table;
bool found_all_fonts = true;
for(const auto& f : fontlist) {
// Ignore fonts if the font file doesn't exist - this matches the behavior of when we
// can't reuse the already-loaded fonts.
if(!check_font_file(f.name))
continue;
const auto& old_record = std::find_if(
family_table.cbegin(), family_table.cend(), [&f](family_record x) { return f.name == x.name; });
if(old_record == family_table.cend()) {
found_all_fonts = false;
break;
}
reordered_family_table.emplace_back(*old_record);
}
if(found_all_fonts) {
std::swap(family_table, reordered_family_table);
DBG_FT << "Reordered the font list, the order is now:\n";
for(const auto& x : family_table) {
DBG_FT << "[" << x.subset << "]:\t\tbase:\t'" << x.name << "'\n";
}
return;
}
}
// The existing fonts weren't sufficient, or this is the first time that this function has been
// called. Load all the fonts from scratch.
clear_fonts();
// To access TTF_GlyphIsProvided, we need to create instances of each font. Choose a size that
// the GUI will want to use.
const auto default_size = preferences::font_scaled(font::SIZE_NORMAL);
for(const auto& f : fontlist) {
if(!check_font_file(f.name))
continue;
// Insert fonts only if the font file exists
const subset_id subset = font_names.size();
font_names.push_back(f.name);
if(f.bold_name && check_font_file(*f.bold_name)) {
bold_names.push_back(*f.bold_name);
} else {
bold_names.emplace_back();
}
if(f.italic_name && check_font_file(*f.italic_name)) {
italic_names.push_back(*f.italic_name);
} else {
italic_names.emplace_back();
}
auto font = sdl_ttf::get_font(font_id{subset, default_size});
family_table.push_back(family_record{std::move(font), subset, f.name});
}
assert(font_names.size() == bold_names.size());
assert(font_names.size() == italic_names.size());
DBG_FT << "Set the font list. The styled font families are:\n";
for(std::size_t i = 0; i < font_names.size(); ++i) {
DBG_FT << "[" << i << "]:\t\tbase:\t'" << font_names[i] << "'\tbold:\t'" << bold_names[i] << "'\titalic:\t'"
<< italic_names[i] << "'\n";
}
}
/**
* Splits the UTF-8 text into text_chunks using the same font.
*
* This uses a greedy-match - once we've found the start of a chunk,
* include as many characters as we can in the same chunk.
*
* If we've got a fallback font that contains all characters, and a
* preferred font that will only contains some of them, this means that
* we minimize the number of times that we switch from one font to the
* other - once we've had to use the fallback, keep using it.
*
* This also means that combining characters such as U+308 or U+FE00 are
* kept with the character that they should be modifying.
*/
std::vector<text_chunk> sdl_ttf::split_text(const std::string& utf8_text)
{
std::vector<text_chunk> chunks;
if(utf8_text.empty())
return chunks;
try {
const auto end = utf8::iterator::end(utf8_text);
auto chunk_start = utf8::iterator(utf8_text);
while(chunk_start != end) {
auto& family = find_font_containing(*chunk_start);
if(family.subset >= 0) {
auto ch = chunk_start;
auto last_in_chunk = chunk_start;
while(ch != end && TTF_GlyphIsProvided(family.font.get(), *ch)) {
last_in_chunk = ch;
++ch;
}
chunks.emplace_back(
family.subset, std::string{chunk_start.substr().first, last_in_chunk.substr().second});
chunk_start = ch;
} else {
++chunk_start;
}
}
} catch(utf8::invalid_utf8_exception&) {
WRN_FT << "Invalid UTF-8 string: \"" << utf8_text << "\"" << std::endl;
}
return chunks;
}
} // end namespace font

View file

@ -1,84 +0,0 @@
/*
Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#pragma once
#include "constants.hpp"
#include "font_id.hpp"
#include "font_description.hpp"
#include "color.hpp"
#include <memory>
#include <string>
#include <SDL2/SDL_ttf.h>
class surface;
namespace font {
// Returns a SDL surface containing the text rendered in a given color.
surface get_rendered_text(const std::string& text, int size, const color_t& color, int style=0);
SDL_Rect draw_text_line(surface& gui_surface, const SDL_Rect& area, int size,
const color_t& color, const std::string& text,
int x, int y, bool use_tooltips, int style);
/**
* Determine the width of a line of text given a certain font size.
* The font type used is the default wesnoth font type.
*/
int line_width(const std::string& line, int font_size, int style=TTF_STYLE_NORMAL);
/**
* Determine the size of a line of text given a certain font size. Similar to
* line_width, but for both coordinates.
*/
SDL_Rect line_size(const std::string& line, int font_size, int style=TTF_STYLE_NORMAL);
/**
* If the text exceeds the specified max width, end it with an ellipsis (...)
*/
std::string make_text_ellipsis(const std::string& text, int font_size, int max_width,
int style = TTF_STYLE_NORMAL);
/***
* Object which initializes and destroys SDL_TTF, and manages caches of open fonts.
*
* This isn't properly self-contained, the .cpp file (and the implementations
* of the other functions in this .hpp file) expect that there will be exactly
* one instance of this object.
*/
struct sdl_ttf
{
sdl_ttf();
~sdl_ttf();
sdl_ttf(const sdl_ttf&) = delete;
sdl_ttf& operator=(const sdl_ttf&) = delete;
// Load a font
static std::shared_ptr<TTF_Font> get_font(font_id);
/**
* Set the list of fonts. The order denotes the priority - if text could be rendered with more than one of these
* fonts, the one given earlier will be used.
*/
static void set_font_list(const std::vector<subset_descriptor>& fontlist);
// Split a utf8 string into text_chunks
static std::vector<text_chunk> split_text(const std::string& utf8_text);
};
} // end namespace font

View file

@ -1,57 +0,0 @@
/*
Copyright (C) 2016 - 2018 by Chris Beck<render787@gmail.com>
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#include "font/text_cache.hpp"
#include "sdl/surface.hpp"
#include <algorithm>
namespace font {
text_cache::text_list text_cache::cache_;
unsigned int text_cache::max_size_ = 50;
void text_cache::resize(unsigned int size)
{
// DBG_FT << "Text cache: resize from: " << max_size_ << " to: "
// << size << " items in cache: " << cache_.size() << '\n';
while(size < cache_.size()) {
cache_.pop_back();
}
max_size_ = size;
}
text_surface &text_cache::find(const text_surface& t)
{
static std::size_t lookup_ = 0, hit_ = 0;
text_list::iterator it_bgn = cache_.begin(), it_end = cache_.end();
text_list::iterator it = std::find(it_bgn, it_end, t);
if (it != it_end) {
cache_.splice(it_bgn, cache_, it);
++hit_;
} else {
if (cache_.size() >= max_size_)
cache_.pop_back();
cache_.push_front(t);
}
if (++lookup_ % 1000 == 0) {
// DBG_FT << "Text cache: " << lookup_ << " lookups, " << (hit_ / 10) << "% hits\n";
hit_ = 0;
}
return cache_.front();
}
} // end namespace font

View file

@ -1,38 +0,0 @@
/*
Copyright (C) 2016 - 2018 by Chris Beck<render787@gmail.com>
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* Note: Specific to sdl_ttf
*/
#pragma once
#include "text_surface.hpp"
#include <list>
namespace font {
class text_cache
{
public:
static text_surface &find(const text_surface& t);
static void resize(unsigned int size);
private:
typedef std::list< text_surface > text_list;
static text_list cache_;
static unsigned int max_size_;
};
} // end namespace font

View file

@ -1,200 +0,0 @@
/*
Copyright (C) 2016 - 2018 by Chris Beck<render787@gmail.com>
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#include "font/text_surface.hpp"
#include "font/sdl_ttf.hpp"
#include "sdl/surface.hpp"
#include "log.hpp"
#include <SDL2/SDL_ttf.h>
#include <string>
#include <vector>
#ifdef HAVE_FRIBIDI
#include <fribidi.h>
#endif
static lg::log_domain log_font("font");
#define DBG_FT LOG_STREAM(debug, log_font)
#define LOG_FT LOG_STREAM(info, log_font)
#define WRN_FT LOG_STREAM(warn, log_font)
#define ERR_FT LOG_STREAM(err, log_font)
namespace font {
#ifdef HAVE_FRIBIDI
void text_surface::bidi_cvt()
{
char *c_str = const_cast<char *>(str_.c_str()); // fribidi forgot const...
FriBidiStrIndex len = str_.length();
FriBidiChar *bidi_logical = new FriBidiChar[len + 2];
FriBidiChar *bidi_visual = new FriBidiChar[len + 2];
char *utf8str = new char[4*len + 1]; //assume worst case here (all 4 Byte characters)
FriBidiCharType base_dir = FRIBIDI_TYPE_ON;
FriBidiStrIndex n;
n = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, c_str, len, bidi_logical);
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
fribidi_log2vis(bidi_logical, n, &base_dir, bidi_visual, nullptr, nullptr, nullptr);
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
fribidi_unicode_to_charset(FRIBIDI_CHAR_SET_UTF8, bidi_visual, n, utf8str);
is_rtl_ = base_dir == FRIBIDI_TYPE_RTL;
str_ = std::string(utf8str);
delete[] bidi_logical;
delete[] bidi_visual;
delete[] utf8str;
}
#endif
text_surface::text_surface(const std::string& str, int size,
color_t color, int style)
: hash_(0)
, font_size_(size)
, color_(color)
, style_(style)
, w_(-1)
, h_(-1)
, str_(str)
, initialized_(false)
, chunks_()
, surfs_()
#ifdef HAVE_FRIBIDI
, is_rtl_(false)
#endif
{
#ifdef HAVE_FRIBIDI
bidi_cvt();
#endif
hash();
}
text_surface::text_surface(int size, color_t color, int style) :
hash_(0),
font_size_(size),
color_(color),
style_(style),
w_(-1),
h_(-1),
str_(),
initialized_(false),
chunks_(),
surfs_()
#ifdef HAVE_FRIBIDI
,is_rtl_(false)
#endif
{
}
void text_surface::set_text(const std::string& str)
{
initialized_ = false;
w_ = -1;
h_ = -1;
str_ = str;
#ifdef HAVE_FRIBIDI
bidi_cvt();
#endif
hash();
}
void text_surface::hash()
{
unsigned int h = 0;
for(const char c : str_) {
h = ((h << 9) | (h >> (sizeof(int) * 8 - 9))) ^ (c);
}
hash_ = h;
}
void text_surface::measure() const
{
w_ = 0;
h_ = 0;
for(const text_chunk& chunk : chunks_)
{
auto ttfont = sdl_ttf::get_font(font_id(chunk.subset, font_size_, style_));
if(ttfont == nullptr) {
continue;
}
int w, h;
TTF_SizeUTF8(ttfont.get(), chunk.text.c_str(), &w, &h);
w_ += w;
h_ = std::max<int>(h_, h);
}
}
std::size_t text_surface::width() const
{
if (w_ == -1) {
if(chunks_.empty())
chunks_ = sdl_ttf::split_text(str_);
measure();
}
return w_;
}
std::size_t text_surface::height() const
{
if (h_ == -1) {
if(chunks_.empty())
chunks_ = sdl_ttf::split_text(str_);
measure();
}
return h_;
}
const std::vector<surface>& text_surface::get_surfaces() const
{
if(initialized_)
return surfs_;
initialized_ = true;
// Impose a maximal number of characters for a text line. Do now draw
// any text longer that that, to prevent a SDL buffer overflow
if(width() > max_text_line_width)
return surfs_;
for(const text_chunk& chunk : chunks_)
{
auto ttfont = sdl_ttf::get_font(font_id(chunk.subset, font_size_, style_));
surface s = surface(TTF_RenderUTF8_Blended(ttfont.get(), chunk.text.c_str(), color_.to_sdl()));
if(s)
surfs_.push_back(s);
}
return surfs_;
}
bool text_surface::operator==(const text_surface& t) const {
return hash_ == t.hash_ && font_size_ == t.font_size_
&& color_ == t.color_ && style_ == t.style_ && str_ == t.str_;
}
} // end namespace font

View file

@ -1,66 +0,0 @@
/*
Copyright (C) 2016 - 2018 by Chris Beck<render787@gmail.com>
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#pragma once
#include "font_id.hpp" // for text_chunk
#include "color.hpp"
#include <SDL2/SDL_ttf.h>
#include <string>
#include <vector>
/***
* Note: This is specific to the SDL_TTF codepath.
*/
class surface;
namespace font {
class text_surface
{
public:
text_surface(const std::string& str, int size, color_t color, int style);
text_surface(int size, color_t color, int style);
void set_text(const std::string& str);
void measure() const;
std::size_t width() const;
std::size_t height() const;
#ifdef HAVE_FRIBIDI
bool is_rtl() const { return is_rtl_; } // Right-To-Left alignment
#endif
const std::vector<surface>& get_surfaces() const;
bool operator==(const text_surface& t) const;
bool operator!=(const text_surface& t) const { return !operator==(t); }
private:
int hash_;
int font_size_;
color_t color_;
int style_;
mutable int w_, h_;
std::string str_;
mutable bool initialized_;
mutable std::vector<text_chunk> chunks_;
mutable std::vector<surface> surfs_;
#ifdef HAVE_FRIBIDI
bool is_rtl_;
void bidi_cvt();
#endif
void hash();
};
} // end namespace font

View file

@ -16,7 +16,6 @@
#include "gui/widgets/text_box.hpp" #include "gui/widgets/text_box.hpp"
#include "font/sdl_ttf.hpp"
#include "gui/core/log.hpp" #include "gui/core/log.hpp"
#include "gui/core/register_widget.hpp" #include "gui/core/register_widget.hpp"
#include "gui/widgets/settings.hpp" #include "gui/widgets/settings.hpp"

View file

@ -31,14 +31,16 @@
#pragma once #pragma once
#include "color.hpp"
#include "exceptions.hpp" // for error #include "exceptions.hpp" // for error
#include "font/sdl_ttf.hpp" // for line_width, relative_size #include "font/constants.hpp"
#include "gettext.hpp" #include "gettext.hpp"
#include <optional> #include <optional>
#include <cstring> #include <cstring>
#include <list> // for list #include <list> // for list
#include <memory> #include <memory>
#include <ostream> // for operator<<, stringstream, etc #include <ostream> // for operator<<, stringstream, etc
#include <sstream>
#include <string> // for string, allocator, etc #include <string> // for string, allocator, etc
#include <utility> // for pair, make_pair #include <utility> // for pair, make_pair
#include <vector> // for vector, etc #include <vector> // for vector, etc

View file

@ -18,6 +18,7 @@
#include "game_config.hpp" // for debug #include "game_config.hpp" // for debug
#include "font/sdl_ttf_compat.hpp" #include "font/sdl_ttf_compat.hpp"
#include "help/help_impl.hpp" // for parse_error, box_width, etc #include "help/help_impl.hpp" // for parse_error, box_width, etc
#include "lexical_cast.hpp"
#include "picture.hpp" // for get_image #include "picture.hpp" // for get_image
#include "log.hpp" // for LOG_STREAM, log_domain, etc #include "log.hpp" // for LOG_STREAM, log_domain, etc
#include "preferences/general.hpp" // for font_scaled #include "preferences/general.hpp" // for font_scaled

View file

@ -16,7 +16,6 @@
#include "help/help_topic_generators.hpp" #include "help/help_topic_generators.hpp"
#include "font/sdl_ttf.hpp" // for line_width
#include "font/sdl_ttf_compat.hpp" #include "font/sdl_ttf_compat.hpp"
#include "game_config.hpp" // for debug, menu_contract, etc #include "game_config.hpp" // for debug, menu_contract, etc
#include "preferences/game.hpp" // for encountered_terrains, etc #include "preferences/game.hpp" // for encountered_terrains, etc

View file

@ -17,7 +17,6 @@
#include "show_dialog.hpp" #include "show_dialog.hpp"
#include "floating_label.hpp" #include "floating_label.hpp"
#include "font/sdl_ttf.hpp"
#include "picture.hpp" #include "picture.hpp"
#include "gettext.hpp" #include "gettext.hpp"
#include "gui/core/event/handler.hpp" #include "gui/core/event/handler.hpp"

View file

@ -17,7 +17,6 @@
#include "widgets/button.hpp" #include "widgets/button.hpp"
#include "filesystem.hpp" #include "filesystem.hpp"
#include "font/sdl_ttf.hpp"
#include "game_config.hpp" #include "game_config.hpp"
#include "game_errors.hpp" #include "game_errors.hpp"
#include "picture.hpp" #include "picture.hpp"

View file

@ -17,9 +17,9 @@
#include "widgets/menu.hpp" #include "widgets/menu.hpp"
#include "game_config.hpp" #include "game_config.hpp"
#include "font/sdl_ttf.hpp"
#include "font/standard_colors.hpp" #include "font/standard_colors.hpp"
#include "language.hpp" #include "language.hpp"
#include "lexical_cast.hpp"
#include "picture.hpp" #include "picture.hpp"
#include "font/marked-up_text.hpp" #include "font/marked-up_text.hpp"
#include "font/sdl_ttf_compat.hpp" #include "font/sdl_ttf_compat.hpp"

View file

@ -18,7 +18,6 @@
#include "cursor.hpp" #include "cursor.hpp"
#include "desktop/clipboard.hpp" #include "desktop/clipboard.hpp"
#include "font/sdl_ttf.hpp"
#include "font/sdl_ttf_compat.hpp" #include "font/sdl_ttf_compat.hpp"
#include "log.hpp" #include "log.hpp"
#include "sdl/rect.hpp" #include "sdl/rect.hpp"