Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 24 additions & 11 deletions src/console_cmds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2319,6 +2319,18 @@ static bool ConContent(std::span<std::string_view> argv)
}
#endif /* defined(WITH_ZLIB) */

/**
* Get string representation of a font load reason.
* @param load_reason The font load reason.
* @return String representation.
*/
static std::string_view FontLoadReasonToName(FontLoadReason load_reason)
{
static const std::string_view LOAD_REASON_TO_NAME[] = { "default", "configured", "language" };
static_assert(std::size(LOAD_REASON_TO_NAME) == to_underlying(FontLoadReason::End));
return LOAD_REASON_TO_NAME[to_underlying(load_reason)];
}

static bool ConFont(std::span<std::string_view> argv)
{
if (argv.empty()) {
Expand Down Expand Up @@ -2367,17 +2379,18 @@ static bool ConFont(std::span<std::string_view> argv)
SetFont(argfs, font, size);
}

for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
FontCache *fc = FontCache::Get(fs);
FontCacheSubSetting *setting = GetFontCacheSubSetting(fs);
/* Make sure all non sprite fonts are loaded. */
if (!setting->font.empty() && !fc->HasParent()) {
FontCache::LoadFontCaches(fs);
fc = FontCache::Get(fs);
}
IConsolePrint(CC_DEFAULT, "{} font:", FontSizeToName(fs));
IConsolePrint(CC_DEFAULT, "Currently active: \"{}\", size {}", fc->GetFontName(), fc->GetFontSize());
IConsolePrint(CC_DEFAULT, "Requested: \"{}\", size {}", setting->font, setting->size);
IConsolePrint(CC_INFO, "Configured fonts:");
for (uint i = 0; FontSize fs : FONTSIZES_ALL) {
const FontCacheSubSetting *setting = GetFontCacheSubSetting(fs);
IConsolePrint(CC_DEFAULT, "{}) {} font: \"{}\", size {}", i, FontSizeToName(fs), setting->font, setting->size);
++i;
}

IConsolePrint(CC_INFO, "Currently active fonts:");
for (uint i = 0; const auto &fc : FontCache::Get()) {
if (fc == nullptr) continue;
IConsolePrint(CC_DEFAULT, "{}) {} font: \"{}\" size {} [{}]", i, FontSizeToName(fc->GetSize()), fc->GetFontName(), fc->GetFontSize(), FontLoadReasonToName(fc->GetFontLoadReason()));
++i;
}

return true;
Expand Down
183 changes: 135 additions & 48 deletions src/fontcache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
/** @file fontcache.cpp Cache for characters from fonts. */

#include "stdafx.h"

#include "core/string_consumer.hpp"
#include "fontcache.h"
#include "blitter/factory.hpp"
#include "gfx_layout.h"
Expand All @@ -17,13 +19,14 @@
#include "viewport_func.h"
#include "window_func.h"
#include "fileio_func.h"
#include "zoom_func.h"

#include "safeguards.h"

/** Default unscaled heights for the different sizes of fonts. */
/* static */ const int FontCache::DEFAULT_FONT_HEIGHT[FS_END] = {10, 6, 18, 10};
/** Default unscaled ascenders for the different sizes of fonts. */
/* static */ const int FontCache::DEFAULT_FONT_ASCENDER[FS_END] = {8, 5, 15, 8};
/* static */ const int FontCache::DEFAULT_FONT_ASCENDER[FS_END] = {8, 6, 15, 8};

FontCacheSettings _fcsettings;

Expand All @@ -33,10 +36,10 @@ FontCacheSettings _fcsettings;
* @param fonttype Font type requested.
* @return FontCache of the font if loaded, or nullptr.
*/
/* static */ std::unique_ptr<FontCache> FontProviderManager::LoadFont(FontSize fs, FontType fonttype)
/* static */ std::unique_ptr<FontCache> FontProviderManager::LoadFont(FontSize fs, FontType fonttype, bool search, const std::string &font_name, std::span<const std::byte> os_handle)
{
for (auto &provider : FontProviderManager::GetProviders()) {
auto fc = provider->LoadFont(fs, fonttype);
auto fc = provider->LoadFont(fs, fonttype, search, font_name, os_handle);
if (fc != nullptr) return fc;
}

Expand All @@ -52,32 +55,44 @@ FontCacheSettings _fcsettings;
* @param callback The function to call to check for missing glyphs.
* @return true if a font has been set, false otherwise.
*/
/* static */ bool FontProviderManager::FindFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback)
/* static */ bool FontProviderManager::FindFallbackFont(const std::string &language_isocode, FontSizes fontsizes, MissingGlyphSearcher *callback)
{
return std::ranges::any_of(FontProviderManager::GetProviders(),
[&](auto *provider) { return provider->FindFallbackFont(settings, language_isocode, callback); });
[&](auto *provider) { return provider->FindFallbackFont(language_isocode, fontsizes, callback); });
}

int FontCache::GetDefaultFontHeight(FontSize fs)
{
return FontCache::DEFAULT_FONT_HEIGHT[fs];
}

/**
* Get the font name of a given font size.
* @param fs The font size to look up.
* @return The font name.
*/
std::string FontCache::GetName(FontSize fs)
/* static */ void FontCache::UpdateCharacterHeight(FontSize fs)
{
FontCache *fc = FontCache::Get(fs);
if (fc != nullptr) {
return fc->GetFontName();
} else {
return "[NULL]";
FontMetrics &metrics = FontCache::metrics[fs];

int ascender = 0;
int descender = 0;

for (const auto &fc : FontCache::caches) {
if (fc == nullptr || fc->fs != fs) continue;
ascender = std::max(ascender, fc->ascender);
descender = std::min(descender, fc->descender);
}

if (ascender == 0 && descender == 0) {
/* It's possible that no font is loaded yet, in which case use default values. */
ascender = ScaleGUITrad(FontCache::DEFAULT_FONT_ASCENDER[fs]);
descender = ScaleGUITrad(FontCache::DEFAULT_FONT_ASCENDER[fs] - FontCache::DEFAULT_FONT_HEIGHT[fs]);
}

metrics.height = ascender - descender;
metrics.baseline = ascender;
}

int FontCache::GetGlyphYOffset()
{
return FontCache::GetFontBaseline(this->fs) - this->ascender;
}

/**
* Get height of a character for a given font size.
Expand All @@ -86,20 +101,22 @@ std::string FontCache::GetName(FontSize fs)
*/
int GetCharacterHeight(FontSize size)
{
return FontCache::Get(size)->GetHeight();
uint height = FontCache::GetCharacterHeight(size);
if (height == 0) height = ScaleGUITrad(FontCache::GetDefaultFontHeight(FS_MONO));
return height;
}


/* static */ std::array<std::unique_ptr<FontCache>, FS_END> FontCache::caches{};
/* static */ FontCache::FontCaches FontCache::caches;
/* static */ std::array<FontCache::FontMetrics, FS_END> FontCache::metrics{};
/* static */ std::array<FontIndex, FS_END> FontCache::default_font_index{};

/**
* Initialise font caches with the base sprite font cache for all sizes.
*/
/* static */ void FontCache::InitializeFontCaches()
{
for (FontSize fs = FS_BEGIN; fs != FS_END; fs++) {
if (FontCache::Get(fs) != nullptr) continue;
FontCache::Register(FontProviderManager::LoadFont(fs, FontType::Sprite));
for (FontSize fs : FONTSIZES_ALL) {
UpdateCharacterHeight(fs);
}
}

Expand Down Expand Up @@ -130,19 +147,13 @@ void SetFont(FontSize fontsize, const std::string &font, uint size)
if (!changed) return;

if (fontsize != FS_MONO) {
/* Try to reload only the modified font. */
FontCacheSettings backup = _fcsettings;
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
if (fs == fontsize) continue;
FontCache *fc = FontCache::Get(fs);
GetFontCacheSubSetting(fs)->font = fc->HasParent() ? fc->GetFontName() : "";
}
/* Check if fallback fonts are needed. */
CheckForMissingGlyphs();
_fcsettings = std::move(backup);
} else {
FontCache::LoadFontCaches(fontsize);
}

FontCache::UpdateCharacterHeight(fontsize);
LoadStringWidthTable(fontsize);
UpdateAllVirtCoords();
ReInitAllWindows(true);
Expand All @@ -156,7 +167,7 @@ void SetFont(FontSize fontsize, const std::string &font, uint size)
*/
static bool IsDefaultFont(const FontCacheSubSetting &setting)
{
return setting.font.empty() && setting.os_handle == nullptr;
return setting.font.empty();
}

/**
Expand Down Expand Up @@ -193,7 +204,7 @@ static std::string GetDefaultTruetypeFont(FontSize fs)
* @param fs Font size.
* @return Full path of default font file.
*/
static std::string GetDefaultTruetypeFontFile([[maybe_unused]] FontSize fs)
std::string GetDefaultTruetypeFontFile([[maybe_unused]] FontSize fs)
{
#if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
/* Find font file. */
Expand All @@ -216,18 +227,54 @@ std::string GetFontCacheFontName(FontSize fs)
return GetDefaultTruetypeFontFile(fs);
}

/**
* Register a FontCache for its font size.
* @param fc FontCache to register.
*/
/* static */ void FontCache::Register(std::unique_ptr<FontCache> &&fc)
/* static */ void FontCache::Register(std::unique_ptr<FontCache> &&fc, FontLoadReason load_reason)
{
if (fc == nullptr) return;

FontSize fs = fc->fs;

fc->parent = std::move(FontCache::caches[fs]);
FontCache::caches[fs] = std::move(fc);
/* Find an empty font cache slot. */
auto it = std::find(std::begin(FontCache::caches), std::end(FontCache::caches), nullptr);
if (it == std::end(FontCache::caches)) it = FontCache::caches.insert(it, nullptr);

/* Set up our font index and make us the default font cache for this font size. */
fc->font_index = static_cast<FontIndex>(std::distance(std::begin(FontCache::caches), it));
fc->load_reason = load_reason;
FontCache::default_font_index[fs] = fc->font_index;

/* Register this font cache in the slot. */
*it = std::move(fc);
}

/**
* Add a fallback font, with optional OS-specific handle.
* @param fontsizes Fontsizes to add fallback to.
* @param name Name of font to add.
* @param handle OS-specific handle or data of font.
*/
/* static */ void FontCache::AddFallback(FontSizes fontsizes, FontLoadReason load_reason, std::string_view name, std::span<const std::byte> os_data)
{
for (FontSize fs : fontsizes) {
GetFontCacheSubSetting(fs)->fallback_fonts.emplace_back(load_reason, std::string{name}, std::vector<std::byte>{os_data.begin(), os_data.end()});
}
}

/* static */ void FontCache::LoadDefaultFonts(FontSize fs)
{
/* Load the sprite font, even if it's not preferred. */
FontCache::Register(FontProviderManager::LoadFont(fs, FontType::Sprite, false, {}, {}), FontLoadReason::Default);
if (!_fcsettings.prefer_sprite) {
/* Load the default truetype font if sprite font is not preferred. */
FontCache::Register(FontProviderManager::LoadFont(fs, FontType::TrueType, false, GetDefaultTruetypeFontFile(fs), {}), FontLoadReason::Default);
}
}

/* static */ void FontCache::LoadFallbackFonts(FontSize fs)
{
const FontCacheSubSetting *setting = GetFontCacheSubSetting(fs);
for (auto it = setting->fallback_fonts.rbegin(); it != setting->fallback_fonts.rend(); ++it) {
FontCache::Register(FontProviderManager::LoadFont(fs, FontType::TrueType, false, it->name, it->os_handle), it->load_reason);
}
}

/**
Expand All @@ -236,17 +283,53 @@ std::string GetFontCacheFontName(FontSize fs)
*/
/* static */ void FontCache::LoadFontCaches(FontSizes fontsizes)
{
FontCache::InitializeFontCaches();
static constexpr std::string_view FALLBACK_FONT = "fallback";
static constexpr std::string_view DEFAULT_FONT = "default";
static constexpr std::initializer_list<std::string_view> extra_prefer_default = {DEFAULT_FONT, FALLBACK_FONT};
static constexpr std::initializer_list<std::string_view> extra_prefer_fallback = {FALLBACK_FONT, DEFAULT_FONT};

for (FontSize fs : fontsizes) {
Layouter::ResetFontCache(fs);
FontCache::default_font_index[fs] = INVALID_FONT_INDEX;
}

/* Unload everything except the sprite font cache. */
while (FontCache::Get(fs)->HasParent()) {
FontCache::caches[fs] = std::move(FontCache::caches[fs]->parent);
/* Remove all existing FontCaches. */
if (fontsizes == FONTSIZES_ALL) {
FontCache::caches.clear();
} else {
for (auto it = std::begin(FontCache::caches); it != std::end(FontCache::caches); ++it) {
if (*it == nullptr) continue;
if (!fontsizes.Test((*it)->fs)) continue;
it->reset();
}
}

for (FontSize fs : fontsizes) {
/* Parse configured fonts, separated by ';' into a list. */
std::vector<std::string_view> fontnames;
StringConsumer consumer(GetFontCacheSubSetting(fs)->font);
do {
auto fontname = StrTrimView(consumer.ReadUntilChar(';', StringConsumer::SKIP_ONE_SEPARATOR), " \t");
if (!fontname.empty()) fontnames.push_back(fontname);
} while (consumer.AnyBytesLeft());

/* Add the default and fallback fonts as lowest priority if not manually specified. */
for (const auto &extra_font : _fcsettings.prefer_default ? extra_prefer_default : extra_prefer_fallback) {
if (std::ranges::find(fontnames, extra_font) == std::end(fontnames)) fontnames.push_back(extra_font);
}

FontCache::Register(FontProviderManager::LoadFont(fs, FontType::TrueType));
/* Load configured fonts in reverse order so that the first entry has priority. */
for (auto it = fontnames.rbegin(); it != fontnames.rend(); ++it) {
if (*it == DEFAULT_FONT) {
FontCache::LoadDefaultFonts(fs);
} else if (*it == FALLBACK_FONT) {
FontCache::LoadFallbackFonts(fs);
} else {
FontCache::Register(FontProviderManager::LoadFont(fs, FontType::TrueType, true, std::string{*it}, {}), FontLoadReason::Configured);
}
}

FontCache::UpdateCharacterHeight(fs);
}
}

Expand All @@ -256,8 +339,14 @@ std::string GetFontCacheFontName(FontSize fs)
*/
/* static */ void FontCache::ClearFontCaches(FontSizes fontsizes)
{
for (const auto &fc : FontCache::caches) {
if (fc == nullptr) continue;
if (!fontsizes.Test(fc->GetSize())) continue;
fc->ClearFontCache();
}

for (FontSize fs : fontsizes) {
FontCache::Get(fs)->ClearFontCache();
FontCache::UpdateCharacterHeight(fs);
}
}

Expand All @@ -266,7 +355,5 @@ std::string GetFontCacheFontName(FontSize fs)
*/
/* static */ void FontCache::UninitializeFontCaches()
{
for (auto &fc : FontCache::caches) {
fc.reset();
}
FontCache::caches.clear();
}
Loading
Loading