Removed the SDL_TTF and GUI1 font rendering API and associated functions

This removes all functions related to:
* GUI1 font formatting
* GUI1 font rendering
* Manual text surface size calculations
* The font description interface. While it wasn't exclusive to the TTF rendering system,
  it was unused by Pango/Cairo rendering.

A huge chunk of the help browser code was commented out since we don't have font::line_width anymore.
Likewise, a hack was added to the GUI2 text box since we don't have have get_max_height anymore.

font::is_cjk_char was retained (as well as some related helpers) since they might be useful
later.

I'll leave it to someone else to update the CMake/Scons files to remove TTF as a build
dependency.
This commit is contained in:
Charles Dang 2018-03-26 14:08:45 +11:00
parent ab67b402d1
commit c198be1fa8
25 changed files with 27 additions and 1768 deletions

File diff suppressed because one or more lines are too long

View file

@ -177,7 +177,7 @@
<LinkLibraryDependencies>true</LinkLibraryDependencies>
</ProjectReference>
<Link>
<AdditionalDependencies>SDL2main.lib;SDL2.lib;SDL2_image.lib;SDL2_ttf.lib;SDL2_mixer.lib;libcrypto.lib;cairo.lib;winmm.lib;ws2_32.lib;pango-1.0.lib;pangocairo-1.0.lib;gobject-2.0.lib;glib-2.0.lib;libpng.lib;$(IntDir)liblua.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>SDL2main.lib;SDL2.lib;SDL2_image.lib;SDL2_mixer.lib;libcrypto.lib;cairo.lib;winmm.lib;ws2_32.lib;pango-1.0.lib;pangocairo-1.0.lib;gobject-2.0.lib;glib-2.0.lib;libpng.lib;$(IntDir)liblua.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\external\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>false</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
@ -1010,13 +1010,6 @@
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Debug|Win32'">$(IntDir)Font\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Release|Win32'">$(IntDir)Font\</ObjectFileName>
</ClCompile>
<ClCompile Include="..\..\src\font\sdl_ttf.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)Font\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='ReleaseDEBUG|Win32'">$(IntDir)Font\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)Font\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Debug|Win32'">$(IntDir)Font\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Release|Win32'">$(IntDir)Font\</ObjectFileName>
</ClCompile>
<ClCompile Include="..\..\src\font\standard_colors.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)Font\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='ReleaseDEBUG|Win32'">$(IntDir)Font\</ObjectFileName>
@ -1031,13 +1024,6 @@
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Debug|Win32'">$(IntDir)Font\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Release|Win32'">$(IntDir)Font\</ObjectFileName>
</ClCompile>
<ClCompile Include="..\..\src\font\text_cache.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)Font\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='ReleaseDEBUG|Win32'">$(IntDir)Font\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)Font\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Debug|Win32'">$(IntDir)Font\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Release|Win32'">$(IntDir)Font\</ObjectFileName>
</ClCompile>
<ClCompile Include="..\..\src\font\text_formatting.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)Font\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='ReleaseDEBUG|Win32'">$(IntDir)Font\</ObjectFileName>
@ -1045,13 +1031,6 @@
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Debug|Win32'">$(IntDir)Font\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Release|Win32'">$(IntDir)Font\</ObjectFileName>
</ClCompile>
<ClCompile Include="..\..\src\font\text_surface.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)Font\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='ReleaseDEBUG|Win32'">$(IntDir)Font\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)Font\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Debug|Win32'">$(IntDir)Font\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Release|Win32'">$(IntDir)Font\</ObjectFileName>
</ClCompile>
<ClCompile Include="..\..\src\format_time_summary.cpp" />
<ClCompile Include="..\..\src\formula\callable_objects.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)Formula\</ObjectFileName>
@ -3597,20 +3576,15 @@
<ClInclude Include="..\..\src\floating_label.hpp" />
<ClInclude Include="..\..\src\font\error.hpp" />
<ClInclude Include="..\..\src\font\font_config.hpp" />
<ClInclude Include="..\..\src\font\font_description.hpp" />
<ClInclude Include="..\..\src\font\font_id.hpp" />
<ClInclude Include="..\..\src\font\font_options.hpp" />
<ClInclude Include="..\..\src\font\marked-up_text.hpp" />
<ClInclude Include="..\..\src\font\pango\escape.hpp" />
<ClInclude Include="..\..\src\font\pango\font.hpp" />
<ClInclude Include="..\..\src\font\pango\hyperlink.hpp" />
<ClInclude Include="..\..\src\font\pango\stream_ops.hpp" />
<ClInclude Include="..\..\src\font\sdl_ttf.hpp" />
<ClInclude Include="..\..\src\font\standard_colors.hpp" />
<ClInclude Include="..\..\src\font\text.hpp" />
<ClInclude Include="..\..\src\font\text_cache.hpp" />
<ClInclude Include="..\..\src\font\text_formatting.hpp" />
<ClInclude Include="..\..\src\font\text_surface.hpp" />
<ClInclude Include="..\..\src\formatter.hpp" />
<ClInclude Include="..\..\src\format_time_summary.hpp" />
<ClInclude Include="..\..\src\formula\callable.hpp" />

View file

@ -1503,24 +1503,15 @@
<ClCompile Include="..\..\src\font\marked-up_text.cpp">
<Filter>Font</Filter>
</ClCompile>
<ClCompile Include="..\..\src\font\sdl_ttf.cpp">
<Filter>Font</Filter>
</ClCompile>
<ClCompile Include="..\..\src\font\standard_colors.cpp">
<Filter>Font</Filter>
</ClCompile>
<ClCompile Include="..\..\src\font\text.cpp">
<Filter>Font</Filter>
</ClCompile>
<ClCompile Include="..\..\src\font\text_cache.cpp">
<Filter>Font</Filter>
</ClCompile>
<ClCompile Include="..\..\src\font\text_formatting.cpp">
<Filter>Font</Filter>
</ClCompile>
<ClCompile Include="..\..\src\font\text_surface.cpp">
<Filter>Font</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ogl\context.cpp">
<Filter>OGL</Filter>
</ClCompile>
@ -2934,36 +2925,21 @@
<ClInclude Include="..\..\src\font\font_config.hpp">
<Filter>Font</Filter>
</ClInclude>
<ClInclude Include="..\..\src\font\font_description.hpp">
<Filter>Font</Filter>
</ClInclude>
<ClInclude Include="..\..\src\font\font_id.hpp">
<Filter>Font</Filter>
</ClInclude>
<ClInclude Include="..\..\src\font\font_options.hpp">
<Filter>Font</Filter>
</ClInclude>
<ClInclude Include="..\..\src\font\marked-up_text.hpp">
<Filter>Font</Filter>
</ClInclude>
<ClInclude Include="..\..\src\font\sdl_ttf.hpp">
<Filter>Font</Filter>
</ClInclude>
<ClInclude Include="..\..\src\font\standard_colors.hpp">
<Filter>Font</Filter>
</ClInclude>
<ClInclude Include="..\..\src\font\text.hpp">
<Filter>Font</Filter>
</ClInclude>
<ClInclude Include="..\..\src\font\text_cache.hpp">
<Filter>Font</Filter>
</ClInclude>
<ClInclude Include="..\..\src\font\text_formatting.hpp">
<Filter>Font</Filter>
</ClInclude>
<ClInclude Include="..\..\src\font\text_surface.hpp">
<Filter>Font</Filter>
</ClInclude>
<ClInclude Include="..\..\src\font\pango\escape.hpp">
<Filter>Font\Pango</Filter>
</ClInclude>

View file

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

View file

@ -27,7 +27,6 @@
#include <SDL.h>
#include <SDL_image.h>
#include <SDL_mixer.h>
#include <SDL_ttf.h>
#include <boost/version.hpp>
@ -206,20 +205,6 @@ version_table_manager::version_table_manager()
names[LIB_SDL_MIXER] = "SDL_mixer";
//
// SDL_ttf
//
SDL_TTF_VERSION(&sdl_version);
compiled[LIB_SDL_TTF] = format_version(sdl_version);
sdl_rt_version = TTF_Linked_Version();
if(sdl_rt_version) {
linked[LIB_SDL_TTF] = format_version(*sdl_rt_version);
}
names[LIB_SDL_TTF] = "SDL_ttf";
//
// Boost
//

View file

@ -25,7 +25,6 @@
#include "fake_unit_manager.hpp"
#include "floating_label.hpp"
#include "font/marked-up_text.hpp"
#include "font/sdl_ttf.hpp"
#include "font/text.hpp"
#include "gettext.hpp"
#include "gui/dialogs/loading_screen.hpp"

View file

@ -103,7 +103,7 @@ void display_chat_manager::add_chat_message(const time_t& time, const std::strin
try {
// We've had a joker who send an invalid utf-8 message to crash clients
// so now catch the exception and ignore the message.
msg = my_disp_.video().faked() ? "" : font::word_wrap_text(msg,font::SIZE_NORMAL,my_disp_.map_outside_area().w*3/4);
//msg = my_disp_.video().faked() ? "" : font::word_wrap_text(msg,font::SIZE_NORMAL,my_disp_.map_outside_area().w*3/4);
} catch (utf8::invalid_utf8_exception&) {
ERR_NG << "Invalid utf-8 found, chat message is ignored." << std::endl;
return;

View file

@ -55,7 +55,7 @@ public:
if (state_.selected) {
sdl::draw_rectangle(location(), {255, 255, 255, 255});
}
font::draw_text(&video(), location(), 16, font::NORMAL_COLOR, desc_.empty() ? id_ : desc_, location().x + 2, location().y, 0);
//font::draw_text(&video(), location(), 16, font::NORMAL_COLOR, desc_.empty() ? id_ : desc_, location().x + 2, location().y, 0);
}
//TODO move to widget

View file

@ -13,9 +13,7 @@
*/
#include "font/font_config.hpp"
#include "font/font_description.hpp"
#include "font/error.hpp"
#include "font/sdl_ttf.hpp"
#include "config.hpp"
#include "log.hpp"
@ -83,20 +81,6 @@ bool check_font_file(std::string name) {
namespace
{
bool add_font_to_fontlist(const config &fonts_config,
std::vector<font::subset_descriptor>& fontlist, const std::string& name)
{
const config &font = fonts_config.find_child("font", "name", name);
if (!font) {
return false;
}
//DBG_FT << "Adding a font record: " << font.debug() << std::endl;
fontlist.push_back(font::subset_descriptor(font));
return true;
}
#ifdef CAIRO_HAS_WIN32_FONT
bool is_valid_font_file(const std::string& file)
{
@ -149,17 +133,6 @@ bool load_font_config()
if (!fonts_config)
return false;
std::set<std::string> known_fonts;
for (const config &font : fonts_config.child_range("font")) {
known_fonts.insert(font["name"]);
if (font.has_attribute("bold_name")) {
known_fonts.insert(font["bold_name"]);
}
if (font.has_attribute("italic_name")) {
known_fonts.insert(font["italic_name"]);
}
}
family_order_sans = fonts_config["family_order"];
family_order_mono = fonts_config["family_order_monospace"];
family_order_light = fonts_config["family_order_light"];
@ -180,21 +153,6 @@ bool load_font_config()
family_order_script = family_order_sans;
}
std::vector<font::subset_descriptor> fontlist;
for(auto font : utils::split(fonts_config["order"])) {
add_font_to_fontlist(fonts_config, fontlist, font);
known_fonts.erase(font);
}
for(auto kfont : known_fonts) {
add_font_to_fontlist(fonts_config, fontlist, kfont);
}
if(fontlist.empty())
return false;
sdl_ttf::set_font_list(fontlist);
return true;
}

View file

@ -21,7 +21,6 @@
*/
#include "font_options.hpp"
#include "sdl_ttf.hpp"
class t_string;
@ -43,9 +42,6 @@ struct manager {
manager & operator = (const manager &) = delete;
private:
/** Initialize sdl_ttf concurrent with font::manager lifetime */
sdl_ttf sdl_ttf_initializer_;
};
/***

View file

@ -1,80 +0,0 @@
/*
Copyright (C) 2015 - 2018 by Chris Beck<render787@gmail.com>
Part of the Battle for Wesnoth Project http://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 "config.hpp"
#include "lexical_cast.hpp"
#include "serialization/string_utils.hpp"
#include <boost/optional.hpp>
#include <string>
#include <utility>
#include <vector>
namespace font {
// structure used to describe a font, and the subset of the Unicode character
// set it covers.
//
// used by font_config interface (not specific to sdl_ttf or pango)
struct subset_descriptor
{
subset_descriptor()
: name()
, bold_name()
, italic_name()
, present_codepoints()
{
}
explicit subset_descriptor(const config & font)
: name(font["name"].str())
, bold_name()
, italic_name()
, present_codepoints()
{
if (font.has_attribute("bold_name")) {
bold_name = font["bold_name"].str();
}
if (font.has_attribute("italic_name")) {
italic_name = font["italic_name"].str();
}
std::vector<std::string> ranges = utils::split(font["codepoints"]);
for (const std::string & i : ranges) {
std::vector<std::string> r = utils::split(i, '-');
if(r.size() == 1) {
std::size_t r1 = lexical_cast_default<std::size_t>(r[0], 0);
present_codepoints.emplace_back(r1, r1);
} else if(r.size() == 2) {
std::size_t r1 = lexical_cast_default<std::size_t>(r[0], 0);
std::size_t r2 = lexical_cast_default<std::size_t>(r[1], 0);
present_codepoints.emplace_back(r1, r2);
}
}
}
std::string name;
boost::optional<std::string> bold_name; //If we are using another font for styled characters in this font, rather than SDL TTF method
boost::optional<std::string> italic_name;
typedef std::pair<int, int> range;
std::vector<range> present_codepoints;
};
} // end namespace font

View file

@ -1,73 +0,0 @@
/*
Copyright (C) 2016 - 2018 by Chris Beck<render787@gmail.com>
Part of the Battle for Wesnoth Project http://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 <SDL_ttf.h>
/***
* Note: This is specific to SDL_TTF code path
*/
namespace font {
// Signed int. Negative values mean "no subset".
typedef int subset_id;
// Used as a key in the font table, which caches the get_font results.
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;
int style;
};
/***
* Text chunk is used by text_surfaces and these are cached sometimes.
*/
struct text_chunk
{
text_chunk(subset_id subset)
: subset(subset)
, 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

@ -18,212 +18,10 @@
* E.g. "@Victory" will be shown in green.
*/
#define GETTEXT_DOMAIN "wesnoth-lib"
#include "font/sdl_ttf.hpp"
#include "gettext.hpp"
#include "font/marked-up_text.hpp"
#include "font/standard_colors.hpp"
#include "sdl/surface.hpp"
#include "serialization/string_utils.hpp"
#include "serialization/unicode.hpp"
#include "video.hpp"
#include "wml_exception.hpp"
#include "preferences/general.hpp"
namespace font {
// NOTE: if you add more markup characters below, you'll need to update
// the list in campaign_server.cpp (illegal_markup_chars) to blacklist
// them for add-on names and titles.
const char LARGE_TEXT='*', SMALL_TEXT='`',
BOLD_TEXT='~', NORMAL_TEXT='{',
NULL_MARKUP='^',
BLACK_TEXT='}', GRAY_TEXT='|',
GOOD_TEXT='@', BAD_TEXT='#',
GREEN_TEXT='@', RED_TEXT='#',
COLOR_TEXT='<', IMAGE='&';
std::string::const_iterator parse_markup(std::string::const_iterator i1,
std::string::const_iterator i2,
int* font_size,
color_t* color, int* style)
namespace font
{
while(i1 != i2) {
switch(*i1) {
case '\\':
// This must either be a quoted special character or a
// quoted backslash - either way, remove leading backslash
break;
case BAD_TEXT:
if (color) *color = BAD_COLOR;
break;
case GOOD_TEXT:
if (color) *color = GOOD_COLOR;
break;
case NORMAL_TEXT:
if (color) *color = NORMAL_COLOR;
break;
case BLACK_TEXT:
if (color) *color = BLACK_COLOR;
break;
case GRAY_TEXT:
if (color) *color = GRAY_COLOR;
break;
case LARGE_TEXT:
if (font_size) *font_size += 2;
break;
case SMALL_TEXT:
if (font_size) *font_size -= 2;
break;
case BOLD_TEXT:
if (style) *style |= TTF_STYLE_BOLD;
break;
case NULL_MARKUP:
return i1+1;
case COLOR_TEXT:
{
std::string::const_iterator start = i1;
// Very primitive parsing for rgb value
// should look like <213,14,151>
++i1;
uint8_t red=0, green=0, blue=0, temp=0;
while (i1 != i2 && *i1 >= '0' && *i1<='9') {
temp*=10;
temp += lexical_cast<int, char>(*i1);
++i1;
}
red=temp;
temp=0;
if (i1 != i2 && ',' == (*i1)) {
++i1;
while(i1 != i2 && *i1 >= '0' && *i1<='9'){
temp*=10;
temp += lexical_cast<int, char>(*i1);
++i1;
}
green=temp;
temp=0;
}
if (i1 != i2 && ',' == (*i1)) {
++i1;
while(i1 != i2 && *i1 >= '0' && *i1<='9'){
temp*=10;
temp += lexical_cast<int, char>(*i1);
++i1;
}
}
blue=temp;
if (i1 != i2 && '>' == (*i1)) {
color_t temp_color {red, green, blue, 0};
if (color) *color = temp_color;
} else {
// stop parsing and do not consume any chars
return start;
}
if (i1 == i2) return i1;
break;
}
default:
return i1;
}
++i1;
}
return i1;
}
std::string del_tags(const std::string& text){
std::vector<std::string> lines = utils::split(text, '\n', 0);
std::vector<std::string>::iterator line;
for(line = lines.begin(); line != lines.end(); ++line) {
std::string::const_iterator i1 = line->begin(),
i2 = line->end();
*line = std::string(parse_markup(i1,i2,nullptr,nullptr,nullptr),i2);
}
return utils::join(lines, "\n");
}
SDL_Rect text_area(const std::string& text, int size, int style)
{
const SDL_Rect area {0,0,10000,10000};
return draw_text(nullptr, area, size, font::NORMAL_COLOR, text, 0, 0, false, style);
}
SDL_Rect draw_text(surface& dst, const SDL_Rect& area, int size,
const color_t& color, const std::string& txt,
int x, int y, bool use_tooltips, int style)
{
// Make sure there's always at least a space,
// so we can ensure that we can return a rectangle for height
static const std::string blank_text(" ");
const std::string& text = txt.empty() ? blank_text : txt;
SDL_Rect res;
res.x = x;
res.y = y;
res.w = 0;
res.h = 0;
std::string::const_iterator i1 = text.begin();
std::string::const_iterator i2 = std::find(i1,text.end(),'\n');
for(;;) {
color_t col = color;
int sz = size;
int text_style = style;
i1 = parse_markup(i1,i2,&sz,&col,&text_style);
if(i1 != i2) {
std::string new_string = utils::unescape(std::string(i1, i2));
const SDL_Rect rect = draw_text_line(dst, area, sz, col, new_string, x, y, use_tooltips, text_style);
if(rect.w > res.w) {
res.w = rect.w;
}
res.h += rect.h;
y += rect.h;
}
if(i2 == text.end()) {
break;
}
i1 = i2+1;
i2 = std::find(i1,text.end(),'\n');
}
return res;
}
SDL_Rect draw_text(CVideo* /*gui*/, const SDL_Rect& area, int size,
const color_t& color, const std::string& txt,
int x, int y, bool use_tooltips, int style)
{
surface null_surf = surface(nullptr);
return draw_text(null_surf, area, size, color, txt, x, y, use_tooltips, style);
}
bool is_format_char(char c)
{
switch(c) {
case LARGE_TEXT:
case SMALL_TEXT:
case GOOD_TEXT:
case BAD_TEXT:
case NORMAL_TEXT:
case BLACK_TEXT:
case GRAY_TEXT:
case BOLD_TEXT:
case NULL_MARKUP:
return true;
default:
return false;
}
}
bool is_cjk_char(const ucs4::char_t ch)
{
/**
@ -278,30 +76,6 @@ bool is_cjk_char(const ucs4::char_t ch)
//Halfwidth and Fullwidth Forms
(ch >= 0xff00 && ch < 0xffef);
}
static void cut_word(std::string& line, std::string& word, int font_size, int style, int max_width)
{
std::string tmp = line;
utf8::iterator tc(word);
bool first = true;
font_size = preferences::font_scaled(font_size);
for(;tc != utf8::iterator::end(word); ++tc) {
tmp.append(tc.substr().first, tc.substr().second);
SDL_Rect tsize = line_size(tmp, font_size, style);
if(tsize.w > max_width) {
const std::string& w = word;
if(line.empty() && first) {
line += std::string(w.begin(), tc.substr().second);
word = std::string(tc.substr().second, w.end());
} else {
line += std::string(w.begin(), tc.substr().first);
word = std::string(tc.substr().first, w.end());
}
break;
}
first = false;
}
}
namespace {
@ -396,121 +170,4 @@ inline bool break_after(const ucs4::char_t ch)
} // end of anon namespace
std::string word_wrap_text(const std::string& unwrapped_text, int font_size,
int max_width, int max_height, int max_lines, bool partial_line)
{
VALIDATE(max_width > 0, _("The maximum text width is less than 1."));
utf8::iterator ch(unwrapped_text);
std::string current_word;
std::string current_line;
std::size_t line_width = 0;
std::size_t current_height = 0;
bool line_break = false;
bool first = true;
bool start_of_line = true;
std::string wrapped_text;
std::string format_string;
color_t color;
int font_sz = font_size;
int style = TTF_STYLE_NORMAL;
utf8::iterator end = utf8::iterator::end(unwrapped_text);
while(1) {
if(start_of_line) {
line_width = 0;
format_string.clear();
while(ch != end && *ch < static_cast<ucs4::char_t>(0x100)
&& is_format_char(*ch) && !ch.next_is_end()) {
format_string.append(ch.substr().first, ch.substr().second);
++ch;
}
// We need to parse the special format characters
// to give the proper font_size and style to line_size()
font_sz = font_size;
style = TTF_STYLE_NORMAL;
parse_markup(format_string.begin(),format_string.end(),&font_sz,&color,&style);
current_line.clear();
start_of_line = false;
}
// If there is no current word, get one
if(current_word.empty() && ch == end) {
break;
} else if(current_word.empty()) {
if(*ch == ' ' || *ch == '\n') {
current_word = *ch;
++ch;
} else {
ucs4::char_t previous = 0;
for(;ch != utf8::iterator::end(unwrapped_text) &&
*ch != ' ' && *ch != '\n'; ++ch) {
if(!current_word.empty() &&
break_before(*ch) &&
!no_break_after(previous))
break;
if(!current_word.empty() &&
break_after(previous) &&
!no_break_before(*ch))
break;
current_word.append(ch.substr().first, ch.substr().second);
previous = *ch;
}
}
}
if(current_word == "\n") {
line_break = true;
current_word.clear();
start_of_line = true;
} else {
const std::size_t word_width = line_size(current_word, preferences::font_scaled(font_sz), style).w;
line_width += word_width;
if(static_cast<long>(line_width) > max_width) {
if (!partial_line && static_cast<long>(word_width) > max_width) {
cut_word(current_line,
current_word, font_sz, style, max_width);
}
if(current_word == " ")
current_word = "";
line_break = true;
} else {
current_line += current_word;
current_word = "";
}
}
if(line_break || (current_word.empty() && ch == end)) {
SDL_Rect size = line_size(current_line, preferences::font_scaled(font_sz), style);
if(max_height > 0 && current_height + size.h >= std::size_t(max_height)) {
return wrapped_text;
}
if(!first) {
wrapped_text += '\n';
}
wrapped_text += format_string + current_line;
current_line.clear();
line_width = 0;
current_height += size.h;
line_break = false;
first = false;
if(--max_lines == 0) {
return wrapped_text;
}
}
}
return wrapped_text;
}
} // end namespace font

View file

@ -16,73 +16,9 @@
#pragma once
#include "color.hpp"
class CVideo;
class surface;
#include <string>
#include "serialization/unicode_types.hpp"
#include <SDL_rect.h>
namespace font {
/** Standard markups for color, size, font, images. */
extern const char LARGE_TEXT, SMALL_TEXT, BOLD_TEXT, NORMAL_TEXT, NULL_MARKUP, BLACK_TEXT, GRAY_TEXT,
GOOD_TEXT, BAD_TEXT, GREEN_TEXT, RED_TEXT, COLOR_TEXT, IMAGE;
/** Parses the markup-tags at the front of a string. */
std::string::const_iterator parse_markup(std::string::const_iterator i1,
std::string::const_iterator i2,
int* font_size,
color_t* color, int* style);
/**
* Function to draw text on a surface.
*
* The text will be clipped to area. If the text runs outside of area
* horizontally, an ellipsis will be displayed at the end of it.
*
* If use_tooltips is true, then text with an ellipsis will have a tooltip
* set for it equivalent to the entire contents of the text.
*
* Some very basic 'markup' will be done on the text:
* - any line beginning in # will be displayed in BAD_COLOR (red)
* - any line beginning in @ will be displayed in GOOD_COLOR (green)
* - any line beginning in + will be displayed with size increased by 2
* - any line beginning in - will be displayed with size decreased by 2
* - any line beginning with 0x0n will be displayed in the color of side n
*
* The above special characters can be quoted using a C-style backslash.
*
* A bounding rectangle of the text is returned. If dst is nullptr, then the
* text will not be drawn, and a bounding rectangle only will be returned.
*/
SDL_Rect draw_text(surface& dst, const SDL_Rect& area, int size,
const color_t& color, const std::string& text,
int x, int y, bool use_tooltips = false, int style = 0);
/** wrapper of the previous function, gui can also be nullptr */
SDL_Rect draw_text(CVideo* gui, const SDL_Rect& area, int size,
const color_t& color, const std::string& text,
int x, int y, bool use_tooltips = false, int style = 0);
/** Calculate the size of a text (in pixels) if it were to be drawn. */
SDL_Rect text_area(const std::string& text, int size, int style=0);
/** Copy string, but without tags at the beginning */
std::string del_tags(const std::string& text);
/**
* Determine if char is one of the special chars used as markup.
*
* @retval true Input-char is a markup-char.
* @retval false Input-char is a normal char.
*/
bool is_format_char(char c);
/**
* Determine if a ucs4::char_t is a CJK character
*
@ -91,14 +27,4 @@ bool is_format_char(char c);
*/
bool is_cjk_char(const ucs4::char_t ch);
/**
* Wrap text.
*
* - If the text exceeds the specified max width, wrap it on a word basis.
* - If this is not possible, e.g. the word is too big to fit, wrap it on a
* - char basis.
*/
std::string word_wrap_text(const std::string& unwrapped_text, int font_size,
int max_width, int max_height = -1, int max_lines = -1, bool partial_line = false);
} // end namespace font

View file

@ -1,585 +0,0 @@
/*
Copyright (C) 2016 - 2018 by Chris Beck<render787@gmail.com>
Part of the Battle for Wesnoth Project http://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 "game_config.hpp"
#include "log.hpp"
#include "font/marked-up_text.hpp"
#include "preferences/general.hpp"
#include "tooltips.hpp"
#include "sdl/rect.hpp"
#include "sdl/surface.hpp"
#include "serialization/unicode.hpp"
#include <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 {
/***
* Caches used to speed up font rendering
*/
// 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
{
TTF_Font* font;
int style;
};
static std::map<font_id, ttf_record> font_table;
static std::vector<std::string> font_names;
static std::vector<std::string> bold_names;
static std::vector<std::string> italic_names;
struct char_block_map
{
char_block_map()
: cbmap()
{
}
typedef std::pair<int, subset_id> block_t;
typedef std::map<int, block_t> cbmap_t;
cbmap_t cbmap;
/** Associates not-associated parts of a range with a new font. */
void insert(int first, int last, subset_id id)
{
if (first > last) return;
cbmap_t::iterator i = cbmap.lower_bound(first);
// At this point, either first <= i->first or i is past the end.
if (i != cbmap.begin()) {
cbmap_t::iterator j = i;
--j;
if (first <= j->second.first /* prev.last */) {
insert(j->second.first + 1, last, id);
return;
}
}
if (i != cbmap.end()) {
if (/* next.first */ i->first <= last) {
insert(first, i->first - 1, id);
return;
}
}
cbmap.emplace(first, block_t(last, id));
}
/**
* Compresses map by merging consecutive ranges with the same font, even
* if there is some unassociated ranges in-between.
*/
void compress()
{
LOG_FT << "Font map size before compression: " << cbmap.size() << " ranges\n";
cbmap_t::iterator i = cbmap.begin(), e = cbmap.end();
while (i != e) {
cbmap_t::iterator j = i;
++j;
if (j == e || i->second.second != j->second.second) {
i = j;
continue;
}
i->second.first = j->second.first;
cbmap.erase(j);
}
LOG_FT << "Font map size after compression: " << cbmap.size() << " ranges\n";
}
subset_id get_id(int ch)
{
cbmap_t::iterator i = cbmap.upper_bound(ch);
// At this point, either ch < i->first or i is past the end.
if (i != cbmap.begin()) {
--i;
if (ch <= i->second.first /* prev.last */)
return i->second.second;
}
return -1;
}
};
static char_block_map char_blocks;
//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;
typedef std::map<std::pair<std::string, int>, TTF_Font*> open_font_cache;
open_font_cache open_fonts;
static TTF_Font* open_font_impl(const std::string & , int);
// A wrapper which caches the results of open_font_impl.
// Note that clear_fonts() is responsible to clean up all of these font pointers,
// so to avoid memory leaks fonts should only be opened from this function.
static TTF_Font* open_font(const std::string& fname, int size)
{
const std::pair<std::string, int> key = std::make_pair(fname, size);
const open_font_cache::iterator it = open_fonts.find(key);
if (it != open_fonts.end()) {
return it->second;
}
TTF_Font* result = open_font_impl(fname, size);
open_fonts.emplace(key, result);
return result;
}
static TTF_Font* open_font_impl(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);
TTF_Font* font = 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 << std::endl;
return font;
}
// 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.
TTF_Font* sdl_ttf::get_font(font_id id)
{
const auto it = font_table.find(id);
if(it != font_table.end()) {
if (it->second.font != nullptr) {
// If we found a valid record, use SDL_TTF to add in the difference
// between its intrinsic style and the desired style.
TTF_SetFontStyle(it->second.font, it->second.style ^ id.style);
}
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 (TTF_Font* font = open_font(italic_names[id.subset], id.size)) {
ttf_record rec {font, TTF_STYLE_ITALIC};
font_table.emplace(id, rec);
return sdl_ttf::get_font(id);
}
}
// Now see if the shipped Bold font is useful and available.
if ((id.style & TTF_STYLE_BOLD) && bold_names[id.subset].size()) {
if (TTF_Font* font = open_font(bold_names[id.subset], id.size)) {
ttf_record rec {font, TTF_STYLE_BOLD};
font_table.emplace(id, rec);
return sdl_ttf::get_font(id);
}
}
// Try just to use the basic version of the font then.
if (font_names[id.subset].size()) {
if(TTF_Font* font = open_font(font_names[id.subset], id.size)) {
ttf_record rec {font, TTF_STYLE_NORMAL};
font_table.emplace(id, rec);
return sdl_ttf::get_font(id);
}
}
// 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, bool use_markup)
{
// 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;
std::string::const_iterator after_markup = use_markup ?
parse_markup(ln->begin(), ln->end(), &sz, nullptr, &text_style) : ln->begin();
text_surface txt_surf(sz, color, text_style);
if (after_markup == ln->end() && (ln+1 != ln_end || lines.begin()+1 == ln_end)) {
// we replace empty line by a space (to have a line height)
// except for the last line if we have several
txt_surf.set_text(" ");
} else if (after_markup == ln->begin()) {
// simple case, no markup to skip
txt_surf.set_text(*ln);
} else {
const std::string line(after_markup,ln->end());
txt_surf.set_text(line);
}
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(create_compatible_surface(surfaces.front().front(),width,height));
if (res.null())
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)
{
// TODO maybe later also to parse markup here, but a lot to check
return render_text(str, size, color, style, false);
}
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.null()) {
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);
// for the main current use, we already parsed markup
surface surface(render_text(etext,size,color,style,false));
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 get_max_height(int size)
{
// Only returns the maximal size of the first font
TTF_Font* const font = sdl_ttf::get_font(font_id(0, size));
if(font == nullptr)
return 0;
return TTF_FontHeight(font);
}
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;
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);
}
return text; // Should not happen
}
void cache_mode(CACHE mode)
{
if(mode == CACHE_LOBBY) {
text_cache::resize(1000);
} else {
text_cache::resize(50);
}
}
/***
* 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() {
for(const auto & i : open_fonts) {
TTF_CloseFont(i.second);
}
open_fonts.clear();
font_table.clear();
font_names.clear();
bold_names.clear();
italic_names.clear();
char_blocks.cbmap.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)
{
clear_fonts();
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();
}
for (const subset_descriptor::range &cp_range : f.present_codepoints) {
char_blocks.insert(cp_range.first, cp_range.second, subset);
}
}
char_blocks.compress();
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.
std::vector<text_chunk> sdl_ttf::split_text(const std::string& utf8_text) {
text_chunk current_chunk(0);
std::vector<text_chunk> chunks;
if (utf8_text.empty())
return chunks;
try {
utf8::iterator ch(utf8_text);
int sub = char_blocks.get_id(*ch);
if (sub >= 0) current_chunk.subset = sub;
for(utf8::iterator end = utf8::iterator::end(utf8_text); ch != end; ++ch)
{
sub = char_blocks.get_id(*ch);
if (sub >= 0 && sub != current_chunk.subset) {
chunks.push_back(current_chunk);
current_chunk.text.clear();
current_chunk.subset = sub;
}
current_chunk.text.append(ch.substr().first, ch.substr().second);
}
if (!current_chunk.text.empty()) {
chunks.push_back(current_chunk);
}
}
catch(utf8::invalid_utf8_exception&) {
WRN_FT << "Invalid UTF-8 string: \"" << utf8_text << "\"" << std::endl;
}
return chunks;
}
} // end namespace font

View file

@ -1,87 +0,0 @@
/*
Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project http://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 <string>
#include <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);
// Returns the maximum height of a font, in pixels
int get_max_height(int size);
///
/// 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);
enum CACHE { CACHE_LOBBY, CACHE_GAME };
void cache_mode(CACHE mode);
/***
* RAII object which initializes and destroys SDL_TTF, and manages caches of open fonts
*/
struct sdl_ttf {
sdl_ttf();
~sdl_ttf();
sdl_ttf(const sdl_ttf &) = delete;
sdl_ttf & operator = (const sdl_ttf &) = delete;
// Load a font
static TTF_Font * get_font(font_id);
// Set the list of fonts
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 http://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 http://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 http://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 <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_)
{
TTF_Font* ttfont = sdl_ttf::get_font(font_id(chunk.subset, font_size_, style_));
if(ttfont == nullptr) {
continue;
}
int w, h;
TTF_SizeUTF8(ttfont, 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_)
{
TTF_Font* ttfont = sdl_ttf::get_font(font_id(chunk.subset, font_size_, style_));
surface s = surface(TTF_RenderUTF8_Blended(ttfont, chunk.text.c_str(), color_.to_sdl()));
if(!s.null())
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 http://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 <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 "font/sdl_ttf.hpp"
#include "gui/core/log.hpp"
#include "gui/core/register_widget.hpp"
#include "gui/widgets/settings.hpp"
@ -277,8 +276,9 @@ void text_box::update_offsets()
const auto conf = cast_config_to<text_box_definition>();
assert(conf);
// FIXME: This should use pango-cairo code path instead of sdl_ttf code path
text_height_ = font::get_max_height(conf->text_font_size);
// FIXME: set this to a proper value! It used to get the max glyph height
// of a certain font via SDL_TTF.
text_height_ = get_height();
wfl::map_formula_callable variables;
variables.add("height", wfl::variant(get_height()));

View file

@ -347,12 +347,14 @@ topic_text &topic_text::operator=(topic_generator *g)
const config& topic_text::parsed_text() const
{
#if 0
if (generator_) {
parsed_text_ = parse_text((*generator_)());
if (--generator_->count == 0)
delete generator_;
generator_ = nullptr;
}
#endif
return parsed_text_;
}
@ -1327,11 +1329,13 @@ std::vector<std::string> split_in_width(const std::string &s, const int font_siz
{
std::vector<std::string> res;
try {
#if 0
const std::string& first_line = font::word_wrap_text(s, font_size, width, -1, 1, true);
res.push_back(first_line);
if(s.size() > first_line.size()) {
res.push_back(s.substr(first_line.size()));
}
#endif
}
catch (utf8::invalid_utf8_exception&)
{
@ -1481,9 +1485,9 @@ unsigned image_width(const std::string &filename)
return 0;
}
void push_tab_pair(std::vector<std::pair<std::string, unsigned int>> &v, const std::string &s)
void push_tab_pair(std::vector<std::pair<std::string, unsigned int>> &/*v*/, const std::string &/*s*/)
{
v.emplace_back(s, font::line_width(s, normal_font_size));
//v.emplace_back(s, font::line_width(s, normal_font_size));
}
std::string generate_table(const table_spec &tab, const unsigned int spacing)

View file

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

View file

@ -14,7 +14,6 @@
#include "help/help_topic_generators.hpp"
#include "font/sdl_ttf.hpp" // for line_width
#include "game_config.hpp" // for debug, menu_contract, etc
#include "preferences/game.hpp" // for encountered_terrains, etc
#include "gettext.hpp" // for _, gettext, N_
@ -218,6 +217,7 @@ std::string terrain_topic_generator::operator()() const {
//Typedef to help with formatting list of traits
typedef std::pair<std::string, std::string> trait_data;
#if 0
//Helper function for printing a list of trait data
static void print_trait_list(std::stringstream & ss, const std::vector<trait_data> & l)
{
@ -229,8 +229,9 @@ static void print_trait_list(std::stringstream & ss, const std::vector<trait_dat
ss << ", " << make_link(l[i].first,l[i].second);
}
}
#endif
std::string unit_topic_generator::operator()() const {
#if 0
// Force the lazy loading to build this unit.
unit_types.build_unit_type(type_, unit_type::FULL);
@ -788,10 +789,12 @@ std::string unit_topic_generator::operator()() const {
WRN_HP << "When building unit help topics, the display object was null and we couldn't get the terrain info we need.\n";
}
return ss.str();
#endif
return "";
}
void unit_topic_generator::push_header(std::vector< item > &row, const std::string& name) const {
row.emplace_back(bold(name), font::line_width(name, normal_font_size, TTF_STYLE_BOLD));
void unit_topic_generator::push_header(std::vector< item > &/*row*/, const std::string& /*name*/) const {
//row.emplace_back(bold(name), font::line_width(name, normal_font_size, TTF_STYLE_BOLD));
}
} // end namespace help

View file

@ -16,7 +16,7 @@
#include "display.hpp"
#include "floating_label.hpp"
#include "font/sdl_ttf.hpp"
#include "font/constants.hpp"
#include "image.hpp"
#include "log.hpp"
#include "ogl/utils.hpp"
@ -440,6 +440,7 @@ int CVideo::set_help_string(const std::string& str)
int size = font::SIZE_LARGE;
#if 0
while(size > 0) {
if(font::line_width(str, size) > get_width()) {
size--;
@ -447,6 +448,7 @@ int CVideo::set_help_string(const std::string& str)
break;
}
}
#endif
const int border = 5;