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:
parent
8a9e861eb3
commit
2dfdc0061d
22 changed files with 6 additions and 1146 deletions
|
@ -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)
|
||||||
|
|
|
@ -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"])
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Add table
Reference in a new issue