1
0
Fork 0

Compare commits

...

5 Commits

Author SHA1 Message Date
Peter Nelson 604a7fb496
Merge d5e0fa7a73 into 10eeba86a6 2025-07-23 19:33:26 +00:00
Peter Nelson 10eeba86a6 Codechange: Simplify/breakout logic for selecting bridge table sprites.
Move various base offsets to separate functions where they can be reused and documented.

No longer rely on coincidences to select the correct data between bridges and aqueducts.
2025-07-23 20:31:15 +01:00
Peter Nelson d99dad9e9e Codechange: Pass bridge pillar palette/sprite by reference. 2025-07-23 20:31:15 +01:00
Peter Nelson d5e0fa7a73
Codechange: Move fallback font detection to FontCacheFactory.
Provides a standard interface instead of relying on defines.
2025-07-20 23:04:05 +01:00
Peter Nelson b32f46f36e
Codechange: Use ProviderManager interface to register FontCache factories.
This removes use of #ifdefs to select the appropriate loader, and also replaces FontCache self-registration.
2025-07-20 23:04:05 +01:00
20 changed files with 662 additions and 563 deletions

View File

@ -187,7 +187,6 @@ add_files(
fios_gui.cpp fios_gui.cpp
fontcache.cpp fontcache.cpp
fontcache.h fontcache.h
fontdetection.h
framerate_gui.cpp framerate_gui.cpp
framerate_type.h framerate_type.h
gamelog.cpp gamelog.cpp

View File

@ -2369,7 +2369,7 @@ static bool ConFont(std::span<std::string_view> argv)
FontCacheSubSetting *setting = GetFontCacheSubSetting(fs); FontCacheSubSetting *setting = GetFontCacheSubSetting(fs);
/* Make sure all non sprite fonts are loaded. */ /* Make sure all non sprite fonts are loaded. */
if (!setting->font.empty() && !fc->HasParent()) { if (!setting->font.empty() && !fc->HasParent()) {
InitFontCache(fs); FontCache::LoadFontCaches(fs);
fc = FontCache::Get(fs); fc = FontCache::Get(fs);
} }
IConsolePrint(CC_DEFAULT, "{} font:", FontSizeToName(fs)); IConsolePrint(CC_DEFAULT, "{} font:", FontSizeToName(fs));

View File

@ -9,10 +9,8 @@
#include "stdafx.h" #include "stdafx.h"
#include "fontcache.h" #include "fontcache.h"
#include "fontdetection.h"
#include "blitter/factory.hpp" #include "blitter/factory.hpp"
#include "gfx_layout.h" #include "gfx_layout.h"
#include "fontcache/spritefontcache.h"
#include "openttd.h" #include "openttd.h"
#include "settings_func.h" #include "settings_func.h"
#include "strings_func.h" #include "strings_func.h"
@ -30,22 +28,38 @@
FontCacheSettings _fcsettings; FontCacheSettings _fcsettings;
/** /**
* Create a new font cache. * Try loading a font with any fontcache factory.
* @param fs The size of the font. * @param fs Font size to load.
* @param fonttype Font type requested.
* @return FontCache of the font if loaded, or nullptr.
*/ */
FontCache::FontCache(FontSize fs) : parent(FontCache::Get(fs)), fs(fs) /* static */ std::unique_ptr<FontCache> FontProviderManager::LoadFont(FontSize fs, FontType fonttype)
{ {
assert(this->parent == nullptr || this->fs == this->parent->fs); for (auto &provider : FontProviderManager::GetProviders()) {
FontCache::caches[this->fs] = this; auto fc = provider->LoadFont(fs, fonttype);
Layouter::ResetFontCache(this->fs); if (fc != nullptr) return fc;
}
return nullptr;
} }
/** Clean everything up. */ /**
FontCache::~FontCache() * We would like to have a fallback font as the current one
* doesn't contain all characters we need.
* This function must set all fonts of settings.
* @param settings the settings to overwrite the fontname of.
* @param language_isocode the language, e.g. en_GB.
* @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)
{ {
assert(this->parent == nullptr || this->fs == this->parent->fs); for (auto &provider : FontProviderManager::GetProviders()) {
FontCache::caches[this->fs] = this->parent; if (provider->FindFallbackFont(settings, language_isocode, callback)) {
Layouter::ResetFontCache(this->fs); return true;
}
}
return false;
} }
int FontCache::GetDefaultFontHeight(FontSize fs) int FontCache::GetDefaultFontHeight(FontSize fs)
@ -80,12 +94,16 @@ int GetCharacterHeight(FontSize size)
} }
/* static */ FontCache *FontCache::caches[FS_END]; /* static */ std::array<std::unique_ptr<FontCache>, FS_END> FontCache::caches{};
/**
* Initialise font caches with the base sprite font cache for all sizes.
*/
/* static */ void FontCache::InitializeFontCaches() /* static */ void FontCache::InitializeFontCaches()
{ {
for (FontSize fs = FS_BEGIN; fs != FS_END; fs++) { for (FontSize fs = FS_BEGIN; fs != FS_END; fs++) {
if (FontCache::caches[fs] == nullptr) new SpriteFontCache(fs); /* FontCache inserts itself into to the cache. */ if (FontCache::Get(fs) != nullptr) continue;
FontCache::Register(FontProviderManager::LoadFont(fs, FontType::Sprite));
} }
} }
@ -126,7 +144,7 @@ void SetFont(FontSize fontsize, const std::string &font, uint size)
CheckForMissingGlyphs(); CheckForMissingGlyphs();
_fcsettings = std::move(backup); _fcsettings = std::move(backup);
} else { } else {
InitFontCache(fontsize); FontCache::LoadFontCaches(fontsize);
} }
LoadStringWidthTable(fontsize); LoadStringWidthTable(fontsize);
@ -136,15 +154,6 @@ void SetFont(FontSize fontsize, const std::string &font, uint size)
if (_save_config) SaveToConfig(); if (_save_config) SaveToConfig();
} }
#ifdef WITH_FREETYPE
extern void LoadFreeTypeFont(FontSize fs);
extern void UninitFreeType();
#elif defined(_WIN32)
extern void LoadWin32Font(FontSize fs);
#elif defined(WITH_COCOA)
extern void LoadCoreTextFont(FontSize fs);
#endif
/** /**
* Test if a font setting uses the default font. * Test if a font setting uses the default font.
* @return true iff the font is not configured and no fallback font data is present. * @return true iff the font is not configured and no fallback font data is present.
@ -211,43 +220,55 @@ std::string GetFontCacheFontName(FontSize fs)
return GetDefaultTruetypeFontFile(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)
{
if (fc == nullptr) return;
FontSize fs = fc->fs;
fc->parent = std::move(FontCache::caches[fs]);
FontCache::caches[fs] = std::move(fc);
}
/** /**
* (Re)initialize the font cache related things, i.e. load the non-sprite fonts. * (Re)initialize the font cache related things, i.e. load the non-sprite fonts.
* @param fontsizes Font sizes to be initialised. * @param fontsizes Font sizes to be initialised.
*/ */
void InitFontCache(FontSizes fontsizes) /* static */ void FontCache::LoadFontCaches(FontSizes fontsizes)
{ {
FontCache::InitializeFontCaches(); FontCache::InitializeFontCaches();
for (FontSize fs : fontsizes) { for (FontSize fs : fontsizes) {
FontCache *fc = FontCache::Get(fs); Layouter::ResetFontCache(fs);
if (fc->HasParent()) delete fc;
#ifdef WITH_FREETYPE /* Unload everything except the sprite font cache. */
LoadFreeTypeFont(fs); while (FontCache::Get(fs)->HasParent()) {
#elif defined(_WIN32) FontCache::caches[fs] = std::move(FontCache::caches[fs]->parent);
LoadWin32Font(fs); }
#elif defined(WITH_COCOA)
LoadCoreTextFont(fs); FontCache::Register(FontProviderManager::LoadFont(fs, FontType::TrueType));
#endif }
}
/**
* Clear cached information for the specified font caches.
* @param fontsizes Font sizes to clear.
*/
/* static */ void FontCache::ClearFontCaches(FontSizes fontsizes)
{
for (FontSize fs : fontsizes) {
FontCache::Get(fs)->ClearFontCache();
} }
} }
/** /**
* Free everything allocated w.r.t. fonts. * Free everything allocated w.r.t. fonts.
*/ */
void UninitFontCache() /* static */ void FontCache::UninitializeFontCaches()
{ {
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) { std::ranges::generate(FontCache::caches, []() { return nullptr; });
while (FontCache::Get(fs) != nullptr) delete FontCache::Get(fs);
}
#ifdef WITH_FREETYPE
UninitFreeType();
#endif /* WITH_FREETYPE */
} }
#if !defined(_WIN32) && !defined(__APPLE__) && !defined(WITH_FONTCONFIG) && !defined(WITH_COCOA)
bool SetFallbackFont(FontCacheSettings *, const std::string &, MissingGlyphSearcher *) { return false; }
#endif /* !defined(_WIN32) && !defined(__APPLE__) && !defined(WITH_FONTCONFIG) && !defined(WITH_COCOA) */

View File

@ -10,6 +10,7 @@
#ifndef FONTCACHE_H #ifndef FONTCACHE_H
#define FONTCACHE_H #define FONTCACHE_H
#include "provider_manager.h"
#include "string_type.h" #include "string_type.h"
#include "spritecache.h" #include "spritecache.h"
@ -20,18 +21,23 @@ static const GlyphID SPRITE_GLYPH = 1U << 30;
/** Font cache for basic fonts. */ /** Font cache for basic fonts. */
class FontCache { class FontCache {
protected: protected:
static FontCache *caches[FS_END]; ///< All the font caches. static std::array<std::unique_ptr<FontCache>, FS_END> caches; ///< All the font caches.
FontCache *parent; ///< The parent of this font cache. std::unique_ptr<FontCache>parent; ///< The parent of this font cache.
const FontSize fs; ///< The size of the font. const FontSize fs; ///< The size of the font.
int height = 0; ///< The height of the font. int height = 0; ///< The height of the font.
int ascender = 0; ///< The ascender value of the font. int ascender = 0; ///< The ascender value of the font.
int descender = 0; ///< The descender value of the font. int descender = 0; ///< The descender value of the font.
FontCache(FontSize fs) : fs(fs) {}
static void Register(std::unique_ptr<FontCache> &&fc);
public: public:
FontCache(FontSize fs); virtual ~FontCache() {}
virtual ~FontCache();
static void InitializeFontCaches(); static void InitializeFontCaches();
static void UninitializeFontCaches();
static void LoadFontCaches(FontSizes fontsizes);
static void ClearFontCaches(FontSizes fontsizes);
/** Default unscaled font heights. */ /** Default unscaled font heights. */
static const int DEFAULT_FONT_HEIGHT[FS_END]; static const int DEFAULT_FONT_HEIGHT[FS_END];
@ -124,7 +130,7 @@ public:
static inline FontCache *Get(FontSize fs) static inline FontCache *Get(FontSize fs)
{ {
assert(fs < FS_END); assert(fs < FS_END);
return FontCache::caches[fs]; return FontCache::caches[fs].get();
} }
static std::string GetName(FontSize fs); static std::string GetName(FontSize fs);
@ -143,13 +149,6 @@ public:
virtual bool IsBuiltInFont() = 0; virtual bool IsBuiltInFont() = 0;
}; };
inline void ClearFontCache(FontSizes fontsizes)
{
for (FontSize fs : fontsizes) {
FontCache::Get(fs)->ClearFontCache();
}
}
/** Get the Sprite for a glyph */ /** Get the Sprite for a glyph */
inline const Sprite *GetGlyph(FontSize size, char32_t key) inline const Sprite *GetGlyph(FontSize size, char32_t key)
{ {
@ -207,12 +206,39 @@ inline FontCacheSubSetting *GetFontCacheSubSetting(FontSize fs)
uint GetFontCacheFontSize(FontSize fs); uint GetFontCacheFontSize(FontSize fs);
std::string GetFontCacheFontName(FontSize fs); std::string GetFontCacheFontName(FontSize fs);
void InitFontCache(FontSizes fontsizes);
void UninitFontCache();
bool GetFontAAState(); bool GetFontAAState();
void SetFont(FontSize fontsize, const std::string &font, uint size); void SetFont(FontSize fontsize, const std::string &font, uint size);
/** Different types of font that can be loaded. */
enum class FontType : uint8_t {
Sprite, ///< Bitmap sprites from GRF files.
TrueType, ///< Scalable TrueType fonts.
};
/** Factory for FontCaches. */
class FontCacheFactory : public BaseProvider<FontCacheFactory> {
public:
FontCacheFactory(std::string_view name, std::string_view description) : BaseProvider<FontCacheFactory>(name, description)
{
ProviderManager<FontCacheFactory>::Register(*this);
}
virtual ~FontCacheFactory()
{
ProviderManager<FontCacheFactory>::Unregister(*this);
}
virtual std::unique_ptr<FontCache> LoadFont(FontSize fs, FontType fonttype) = 0;
virtual bool FindFallbackFont(struct FontCacheSettings *settings, const std::string &language_isocode, class MissingGlyphSearcher *callback) = 0;
};
class FontProviderManager : ProviderManager<FontCacheFactory> {
public:
static std::unique_ptr<FontCache> LoadFont(FontSize fs, FontType fonttype);
static bool FindFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback);
};
/* Implemented in spritefontcache.cpp */ /* Implemented in spritefontcache.cpp */
void InitializeUnicodeGlyphMap(); void InitializeUnicodeGlyphMap();
void SetUnicodeGlyph(FontSize size, char32_t key, SpriteID sprite); void SetUnicodeGlyph(FontSize size, char32_t key, SpriteID sprite);

View File

@ -8,14 +8,14 @@
/** @file freetypefontcache.cpp FreeType font cache implementation. */ /** @file freetypefontcache.cpp FreeType font cache implementation. */
#include "../stdafx.h" #include "../stdafx.h"
#include "../debug.h" #include "../debug.h"
#include "../fontcache.h" #include "../fontcache.h"
#include "../fontdetection.h"
#include "../blitter/factory.hpp" #include "../blitter/factory.hpp"
#include "../core/math_func.hpp"
#include "../zoom_func.h" #include "../zoom_func.h"
#include "../fileio_func.h" #include "../fileio_func.h"
#include "../error_func.h" #include "../error_func.h"
#include "../../os/unix/font_unix.h"
#include "truetypefontcache.h" #include "truetypefontcache.h"
#include "../table/control_codes.h" #include "../table/control_codes.h"
@ -46,9 +46,6 @@ public:
const void *GetOSHandle() override { return &face; } const void *GetOSHandle() override { return &face; }
}; };
FT_Library _ft_library = nullptr;
/** /**
* Create a new FreeTypeFontCache. * Create a new FreeTypeFontCache.
* @param fs The font size that is going to be cached. * @param fs The font size that is going to be cached.
@ -113,93 +110,6 @@ void FreeTypeFontCache::SetFontSize(int pixels)
} }
} }
static FT_Error LoadFont(FontSize fs, FT_Face face, std::string_view font_name, uint size)
{
Debug(fontcache, 2, "Requested '{}', using '{} {}'", font_name, face->family_name, face->style_name);
/* Attempt to select the unicode character map */
FT_Error error = FT_Select_Charmap(face, ft_encoding_unicode);
if (error == FT_Err_Ok) goto found_face; // Success
if (error == FT_Err_Invalid_CharMap_Handle) {
/* Try to pick a different character map instead. We default to
* the first map, but platform_id 0 encoding_id 0 should also
* be unicode (strange system...) */
FT_CharMap found = face->charmaps[0];
int i;
for (i = 0; i < face->num_charmaps; i++) {
FT_CharMap charmap = face->charmaps[i];
if (charmap->platform_id == 0 && charmap->encoding_id == 0) {
found = charmap;
}
}
if (found != nullptr) {
error = FT_Set_Charmap(face, found);
if (error == FT_Err_Ok) goto found_face;
}
}
FT_Done_Face(face);
return error;
found_face:
new FreeTypeFontCache(fs, face, size);
return FT_Err_Ok;
}
/**
* Loads the freetype font.
* First type to load the fontname as if it were a path. If that fails,
* try to resolve the filename of the font using fontconfig, where the
* format is 'font family name' or 'font family name, font style'.
* @param fs The font size to load.
*/
void LoadFreeTypeFont(FontSize fs)
{
FontCacheSubSetting *settings = GetFontCacheSubSetting(fs);
std::string font = GetFontCacheFontName(fs);
if (font.empty()) return;
if (_ft_library == nullptr) {
if (FT_Init_FreeType(&_ft_library) != FT_Err_Ok) {
ShowInfo("Unable to initialize FreeType, using sprite fonts instead");
return;
}
Debug(fontcache, 2, "Initialized");
}
FT_Face face = nullptr;
/* If font is an absolute path to a ttf, try loading that first. */
int32_t index = 0;
if (settings->os_handle != nullptr) index = *static_cast<const int32_t *>(settings->os_handle);
FT_Error error = FT_New_Face(_ft_library, font.c_str(), index, &face);
if (error != FT_Err_Ok) {
/* Check if font is a relative filename in one of our search-paths. */
std::string full_font = FioFindFullPath(BASE_DIR, font);
if (!full_font.empty()) {
error = FT_New_Face(_ft_library, full_font.c_str(), 0, &face);
}
}
/* Try loading based on font face name (OS-wide fonts). */
if (error != FT_Err_Ok) error = GetFontByFaceName(font, &face);
if (error == FT_Err_Ok) {
error = LoadFont(fs, face, font, GetFontCacheFontSize(fs));
if (error != FT_Err_Ok) {
ShowInfo("Unable to use '{}' for {} font, FreeType reported error 0x{:X}, using sprite font instead", font, FontSizeToName(fs), error);
}
} else {
FT_Done_Face(face);
}
}
/** /**
* Free everything that was allocated for this font cache. * Free everything that was allocated for this font cache.
*/ */
@ -296,19 +206,116 @@ GlyphID FreeTypeFontCache::MapCharToGlyph(char32_t key, bool allow_fallback)
return glyph; return glyph;
} }
/** FT_Library _ft_library = nullptr;
* Free everything allocated w.r.t. freetype.
*/
void UninitFreeType()
{
FT_Done_FreeType(_ft_library);
_ft_library = nullptr;
}
#if !defined(WITH_FONTCONFIG) class FreeTypeFontCacheFactory : public FontCacheFactory {
public:
FreeTypeFontCacheFactory() : FontCacheFactory("freetype", "FreeType font provider") {}
FT_Error GetFontByFaceName(std::string_view font_name, FT_Face *face) { return FT_Err_Cannot_Open_Resource; } virtual ~FreeTypeFontCacheFactory()
{
FT_Done_FreeType(_ft_library);
_ft_library = nullptr;
}
#endif /* !defined(WITH_FONTCONFIG) */ /**
* Loads the freetype font.
* First type to load the fontname as if it were a path. If that fails,
* try to resolve the filename of the font using fontconfig, where the
* format is 'font family name' or 'font family name, font style'.
* @param fs The font size to load.
*/
std::unique_ptr<FontCache> LoadFont(FontSize fs, FontType fonttype) override
{
if (fonttype != FontType::TrueType) return nullptr;
FontCacheSubSetting *settings = GetFontCacheSubSetting(fs);
std::string font = GetFontCacheFontName(fs);
if (font.empty()) return nullptr;
if (_ft_library == nullptr) {
if (FT_Init_FreeType(&_ft_library) != FT_Err_Ok) {
ShowInfo("Unable to initialize FreeType, using sprite fonts instead");
return nullptr;
}
Debug(fontcache, 2, "Initialized");
}
FT_Face face = nullptr;
/* If font is an absolute path to a ttf, try loading that first. */
int32_t index = 0;
if (settings->os_handle != nullptr) index = *static_cast<const int32_t *>(settings->os_handle);
FT_Error error = FT_New_Face(_ft_library, font.c_str(), index, &face);
if (error != FT_Err_Ok) {
/* Check if font is a relative filename in one of our search-paths. */
std::string full_font = FioFindFullPath(BASE_DIR, font);
if (!full_font.empty()) {
error = FT_New_Face(_ft_library, full_font.c_str(), 0, &face);
}
}
#ifdef WITH_FONTCONFIG
/* Try loading based on font face name (OS-wide fonts). */
if (error != FT_Err_Ok) error = GetFontByFaceName(font, &face);
#endif /* WITH_FONTCONFIG */
if (error != FT_Err_Ok) {
FT_Done_Face(face);
return nullptr;
}
return LoadFont(fs, face, font, GetFontCacheFontSize(fs));
}
bool FindFallbackFont(struct FontCacheSettings *settings, const std::string &language_isocode, class MissingGlyphSearcher *callback) override
{
#ifdef WITH_FONTCONFIG
if (FontConfigFindFallbackFont(settings, language_isocode, callback)) return true;
#endif /* WITH_FONTCONFIG */
return false;
}
private:
static std::unique_ptr<FontCache> LoadFont(FontSize fs, FT_Face face, std::string_view font_name, uint size)
{
Debug(fontcache, 2, "Requested '{}', using '{} {}'", font_name, face->family_name, face->style_name);
/* Attempt to select the unicode character map */
FT_Error error = FT_Select_Charmap(face, ft_encoding_unicode);
if (error == FT_Err_Invalid_CharMap_Handle) {
/* Try to pick a different character map instead. We default to
* the first map, but platform_id 0 encoding_id 0 should also
* be unicode (strange system...) */
FT_CharMap found = face->charmaps[0];
for (int i = 0; i < face->num_charmaps; ++i) {
FT_CharMap charmap = face->charmaps[i];
if (charmap->platform_id == 0 && charmap->encoding_id == 0) {
found = charmap;
}
}
if (found != nullptr) {
error = FT_Set_Charmap(face, found);
}
}
if (error != FT_Err_Ok) {
FT_Done_Face(face);
ShowInfo("Unable to use '{}' for {} font, FreeType reported error 0x{:X}, using sprite font instead", font_name, FontSizeToName(fs), error);
return nullptr;
}
return std::make_unique<FreeTypeFontCache>(fs, face, size);
}
};
static FreeTypeFontCacheFactory s_freetype_fontcache_factory;
#endif /* WITH_FREETYPE */ #endif /* WITH_FREETYPE */

View File

@ -151,3 +151,22 @@ bool SpriteFontCache::GetDrawGlyphShadow()
{ {
return false; return false;
} }
class SpriteFontCacheFactory : public FontCacheFactory {
public:
SpriteFontCacheFactory() : FontCacheFactory("sprite", "Sprite font provider") {}
std::unique_ptr<FontCache> LoadFont(FontSize fs, FontType fonttype) override
{
if (fonttype != FontType::Sprite) return nullptr;
return std::make_unique<SpriteFontCache>(fs);
}
bool FindFallbackFont(struct FontCacheSettings *, const std::string &, class MissingGlyphSearcher *) override
{
return false;
}
};
static SpriteFontCacheFactory s_sprite_fontcache_factory;

View File

@ -1,41 +0,0 @@
/*
* This file is part of OpenTTD.
* OpenTTD 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, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file fontdetection.h Functions related to detecting/finding the right font. */
#ifndef FONTDETECTION_H
#define FONTDETECTION_H
#include "fontcache.h"
#ifdef WITH_FREETYPE
#include <ft2build.h>
#include FT_FREETYPE_H
/**
* Load a freetype font face with the given font name.
* @param font_name The name of the font to load.
* @param face The face that has been found.
* @return The error we encountered.
*/
FT_Error GetFontByFaceName(std::string_view font_name, FT_Face *face);
#endif /* WITH_FREETYPE */
/**
* We would like to have a fallback font as the current one
* doesn't contain all characters we need.
* This function must set all fonts of settings.
* @param settings the settings to overwrite the fontname of.
* @param language_isocode the language, e.g. en_GB.
* @param callback The function to call to check for missing glyphs.
* @return true if a font has been set, false otherwise.
*/
bool SetFallbackFont(struct FontCacheSettings *settings, const std::string &language_isocode, class MissingGlyphSearcher *callback);
#endif

View File

@ -1253,7 +1253,7 @@ static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode,
*/ */
void LoadStringWidthTable(FontSizes fontsizes) void LoadStringWidthTable(FontSizes fontsizes)
{ {
ClearFontCache(fontsizes); FontCache::ClearFontCaches(fontsizes);
for (FontSize fs : fontsizes) { for (FontSize fs : fontsizes) {
for (uint i = 0; i != 224; i++) { for (uint i = 0; i != 224; i++) {
@ -1820,7 +1820,7 @@ bool AdjustGUIZoom(bool automatic)
if (old_font_zoom != _font_zoom) { if (old_font_zoom != _font_zoom) {
GfxClearFontSpriteCache(); GfxClearFontSpriteCache();
} }
ClearFontCache(FONTSIZES_ALL); FontCache::ClearFontCaches(FONTSIZES_ALL);
LoadStringWidthTable(); LoadStringWidthTable();
SetupWidgetDimensions(); SetupWidgetDimensions();

View File

@ -245,7 +245,7 @@ static void RealChangeBlitter(std::string_view repl_blitter)
/* Clear caches that might have sprites for another blitter. */ /* Clear caches that might have sprites for another blitter. */
VideoDriver::GetInstance()->ClearSystemSprites(); VideoDriver::GetInstance()->ClearSystemSprites();
ClearFontCache(FONTSIZES_ALL); FontCache::ClearFontCaches(FONTSIZES_ALL);
GfxClearSpriteCache(); GfxClearSpriteCache();
ReInitAllWindows(false); ReInitAllWindows(false);
} }
@ -326,7 +326,7 @@ void CheckBlitter()
{ {
if (!SwitchNewGRFBlitter()) return; if (!SwitchNewGRFBlitter()) return;
ClearFontCache(FONTSIZES_ALL); FontCache::ClearFontCaches(FONTSIZES_ALL);
GfxClearSpriteCache(); GfxClearSpriteCache();
ReInitAllWindows(false); ReInitAllWindows(false);
} }
@ -338,7 +338,7 @@ void GfxLoadSprites()
SwitchNewGRFBlitter(); SwitchNewGRFBlitter();
VideoDriver::GetInstance()->ClearSystemSprites(); VideoDriver::GetInstance()->ClearSystemSprites();
ClearFontCache(FONTSIZES_ALL); FontCache::ClearFontCaches(FONTSIZES_ALL);
GfxInitSpriteMem(); GfxInitSpriteMem();
LoadSpriteTables(); LoadSpriteTables();
GfxInitPalettes(); GfxInitPalettes();

View File

@ -315,7 +315,7 @@ static void ShutdownGame()
/* No NewGRFs were loaded when it was still bootstrapping. */ /* No NewGRFs were loaded when it was still bootstrapping. */
if (_game_mode != GM_BOOTSTRAP) ResetNewGRFData(); if (_game_mode != GM_BOOTSTRAP) ResetNewGRFData();
UninitFontCache(); FontCache::UninitializeFontCaches();
} }
/** /**
@ -700,7 +700,7 @@ int openttd_main(std::span<std::string_view> arguments)
InitializeLanguagePacks(); InitializeLanguagePacks();
/* Initialize the font cache */ /* Initialize the font cache */
InitFontCache(FONTSIZES_REQUIRED); FontCache::LoadFontCaches(FONTSIZES_REQUIRED);
/* This must be done early, since functions use the SetWindowDirty* calls */ /* This must be done early, since functions use the SetWindowDirty* calls */
InitWindowSystem(); InitWindowSystem();

View File

@ -14,7 +14,6 @@
#include "../../blitter/factory.hpp" #include "../../blitter/factory.hpp"
#include "../../error_func.h" #include "../../error_func.h"
#include "../../fileio_func.h" #include "../../fileio_func.h"
#include "../../fontdetection.h"
#include "../../string_func.h" #include "../../string_func.h"
#include "../../strings_func.h" #include "../../strings_func.h"
#include "../../zoom_func.h" #include "../../zoom_func.h"
@ -24,91 +23,6 @@
#include "../../safeguards.h" #include "../../safeguards.h"
bool SetFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback)
{
/* Determine fallback font using CoreText. This uses the language isocode
* to find a suitable font. CoreText is available from 10.5 onwards. */
std::string lang;
if (language_isocode == "zh_TW") {
/* Traditional Chinese */
lang = "zh-Hant";
} else if (language_isocode == "zh_CN") {
/* Simplified Chinese */
lang = "zh-Hans";
} else {
/* Just copy the first part of the isocode. */
lang = language_isocode.substr(0, language_isocode.find('_'));
}
/* Create a font descriptor matching the wanted language and latin (english) glyphs.
* Can't use CFAutoRelease here for everything due to the way the dictionary has to be created. */
CFStringRef lang_codes[2];
lang_codes[0] = CFStringCreateWithCString(kCFAllocatorDefault, lang.c_str(), kCFStringEncodingUTF8);
lang_codes[1] = CFSTR("en");
CFArrayRef lang_arr = CFArrayCreate(kCFAllocatorDefault, (const void **)lang_codes, lengthof(lang_codes), &kCFTypeArrayCallBacks);
CFAutoRelease<CFDictionaryRef> lang_attribs(CFDictionaryCreate(kCFAllocatorDefault, const_cast<const void **>(reinterpret_cast<const void *const *>(&kCTFontLanguagesAttribute)), (const void **)&lang_arr, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
CFAutoRelease<CTFontDescriptorRef> lang_desc(CTFontDescriptorCreateWithAttributes(lang_attribs.get()));
CFRelease(lang_arr);
CFRelease(lang_codes[0]);
/* Get array of all font descriptors for the wanted language. */
CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault, const_cast<const void **>(reinterpret_cast<const void *const *>(&kCTFontLanguagesAttribute)), 1, &kCFTypeSetCallBacks));
CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(lang_desc.get(), mandatory_attribs.get()));
bool result = false;
for (int tries = 0; tries < 2; tries++) {
for (CFIndex i = 0; descs.get() != nullptr && i < CFArrayGetCount(descs.get()); i++) {
CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), i);
/* Get font traits. */
CFAutoRelease<CFDictionaryRef> traits((CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute));
CTFontSymbolicTraits symbolic_traits;
CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits);
/* Skip symbol fonts and vertical fonts. */
if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait)) continue;
/* Skip bold fonts (especially Arial Bold, which looks worse than regular Arial). */
if (symbolic_traits & kCTFontBoldTrait) continue;
/* Select monospaced fonts if asked for. */
if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != callback->Monospace()) continue;
/* Get font name. */
char buffer[128];
CFAutoRelease<CFStringRef> font_name((CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontDisplayNameAttribute));
CFStringGetCString(font_name.get(), buffer, std::size(buffer), kCFStringEncodingUTF8);
/* Serif fonts usually look worse on-screen with only small
* font sizes. As such, we try for a sans-serif font first.
* If we can't find one in the first try, try all fonts. */
if (tries == 0 && (symbolic_traits & kCTFontClassMaskTrait) != (CTFontStylisticClass)kCTFontSansSerifClass) continue;
/* There are some special fonts starting with an '.' and the last
* resort font that aren't usable. Skip them. */
std::string_view name{buffer};
if (name.starts_with(".") || name.starts_with("LastResort")) continue;
/* Save result. */
callback->SetFontNames(settings, name);
if (!callback->FindMissingGlyphs()) {
Debug(fontcache, 2, "CT-Font for {}: {}", language_isocode, name);
result = true;
break;
}
}
}
if (!result) {
/* For some OS versions, the font 'Arial Unicode MS' does not report all languages it
* supports. If we didn't find any other font, just try it, maybe we get lucky. */
callback->SetFontNames(settings, "Arial Unicode MS");
result = !callback->FindMissingGlyphs();
}
callback->FindMissingGlyphs();
return result;
}
CoreTextFontCache::CoreTextFontCache(FontSize fs, CFAutoRelease<CTFontDescriptorRef> &&font, int pixels) : TrueTypeFontCache(fs, pixels), font_desc(std::move(font)) CoreTextFontCache::CoreTextFontCache(FontSize fs, CFAutoRelease<CTFontDescriptorRef> &&font, int pixels) : TrueTypeFontCache(fs, pixels), font_desc(std::move(font))
{ {
this->SetFontSize(pixels); this->SetFontSize(pixels);
@ -287,88 +201,182 @@ const Sprite *CoreTextFontCache::InternalGetGlyph(GlyphID key, bool use_aa)
return this->SetGlyphPtr(key, std::move(new_glyph)).GetSprite(); return this->SetGlyphPtr(key, std::move(new_glyph)).GetSprite();
} }
static CTFontDescriptorRef LoadFontFromFile(const std::string &font_name) class CoreTextFontCacheFactory : public FontCacheFactory {
{ public:
if (!MacOSVersionIsAtLeast(10, 6, 0)) return nullptr; CoreTextFontCacheFactory() : FontCacheFactory("coretext", "CoreText font loader") {}
/* Might be a font file name, try load it. Direct font loading is /**
* only supported starting on OSX 10.6. */ * Loads the TrueType font.
CFAutoRelease<CFStringRef> path; * If a CoreText font description is present, e.g. from the automatic font
* fallback search, use it. Otherwise, try to resolve it by font name.
* @param fs The font size to load.
*/
std::unique_ptr<FontCache> LoadFont(FontSize fs, FontType fonttype) override
{
if (fonttype != FontType::TrueType) return nullptr;
/* See if this is an absolute path. */ FontCacheSubSetting *settings = GetFontCacheSubSetting(fs);
if (FileExists(font_name)) {
path.reset(CFStringCreateWithCString(kCFAllocatorDefault, font_name.c_str(), kCFStringEncodingUTF8)); std::string font = GetFontCacheFontName(fs);
} else { if (font.empty()) return nullptr;
/* Scan the search-paths to see if it can be found. */
std::string full_font = FioFindFullPath(BASE_DIR, font_name); CFAutoRelease<CTFontDescriptorRef> font_ref;
if (!full_font.empty()) {
path.reset(CFStringCreateWithCString(kCFAllocatorDefault, full_font.c_str(), kCFStringEncodingUTF8)); if (settings->os_handle != nullptr) {
font_ref.reset(static_cast<CTFontDescriptorRef>(const_cast<void *>(settings->os_handle)));
CFRetain(font_ref.get()); // Increase ref count to match a later release.
} }
}
if (path) { if (!font_ref && MacOSVersionIsAtLeast(10, 6, 0)) {
/* Try getting a font descriptor to see if the system can use it. */ /* Might be a font file name, try load it. */
CFAutoRelease<CFURLRef> url(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path.get(), kCFURLPOSIXPathStyle, false)); font_ref.reset(LoadFontFromFile(font));
CFAutoRelease<CFArrayRef> descs(CTFontManagerCreateFontDescriptorsFromURL(url.get())); if (!font_ref) ShowInfo("Unable to load file '{}' for {} font, using default OS font selection instead", font, FontSizeToName(fs));
if (descs && CFArrayGetCount(descs.get()) > 0) {
CTFontDescriptorRef font_ref = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0);
CFRetain(font_ref);
return font_ref;
} }
}
return nullptr; if (!font_ref) {
} CFAutoRelease<CFStringRef> name(CFStringCreateWithCString(kCFAllocatorDefault, font.c_str(), kCFStringEncodingUTF8));
/** /* Simply creating the font using CTFontCreateWithNameAndSize will *always* return
* Loads the TrueType font. * something, no matter the name. As such, we can't use it to check for existence.
* If a CoreText font description is present, e.g. from the automatic font * We instead query the list of all font descriptors that match the given name which
* fallback search, use it. Otherwise, try to resolve it by font name. * does not do this stupid name fallback. */
* @param fs The font size to load. CFAutoRelease<CTFontDescriptorRef> name_desc(CTFontDescriptorCreateWithNameAndSize(name.get(), 0.0));
*/ CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault, const_cast<const void **>(reinterpret_cast<const void * const *>(&kCTFontNameAttribute)), 1, &kCFTypeSetCallBacks));
void LoadCoreTextFont(FontSize fs) CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(name_desc.get(), mandatory_attribs.get()));
{
FontCacheSubSetting *settings = GetFontCacheSubSetting(fs);
std::string font = GetFontCacheFontName(fs); /* Assume the first result is the one we want. */
if (font.empty()) return; if (descs && CFArrayGetCount(descs.get()) > 0) {
font_ref.reset((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0));
CFAutoRelease<CTFontDescriptorRef> font_ref; CFRetain(font_ref.get());
}
if (settings->os_handle != nullptr) {
font_ref.reset(static_cast<CTFontDescriptorRef>(const_cast<void *>(settings->os_handle)));
CFRetain(font_ref.get()); // Increase ref count to match a later release.
}
if (!font_ref && MacOSVersionIsAtLeast(10, 6, 0)) {
/* Might be a font file name, try load it. */
font_ref.reset(LoadFontFromFile(font));
if (!font_ref) ShowInfo("Unable to load file '{}' for {} font, using default OS font selection instead", font, FontSizeToName(fs));
}
if (!font_ref) {
CFAutoRelease<CFStringRef> name(CFStringCreateWithCString(kCFAllocatorDefault, font.c_str(), kCFStringEncodingUTF8));
/* Simply creating the font using CTFontCreateWithNameAndSize will *always* return
* something, no matter the name. As such, we can't use it to check for existence.
* We instead query the list of all font descriptors that match the given name which
* does not do this stupid name fallback. */
CFAutoRelease<CTFontDescriptorRef> name_desc(CTFontDescriptorCreateWithNameAndSize(name.get(), 0.0));
CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault, const_cast<const void **>(reinterpret_cast<const void * const *>(&kCTFontNameAttribute)), 1, &kCFTypeSetCallBacks));
CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(name_desc.get(), mandatory_attribs.get()));
/* Assume the first result is the one we want. */
if (descs && CFArrayGetCount(descs.get()) > 0) {
font_ref.reset((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0));
CFRetain(font_ref.get());
} }
if (!font_ref) {
ShowInfo("Unable to use '{}' for {} font, using sprite font instead", font, FontSizeToName(fs));
return nullptr;
}
return std::make_unique<CoreTextFontCache>(fs, std::move(font_ref), GetFontCacheFontSize(fs));
} }
if (!font_ref) { bool FindFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback) override
ShowInfo("Unable to use '{}' for {} font, using sprite font instead", font, FontSizeToName(fs)); {
return; /* Determine fallback font using CoreText. This uses the language isocode
* to find a suitable font. CoreText is available from 10.5 onwards. */
std::string lang;
if (language_isocode == "zh_TW") {
/* Traditional Chinese */
lang = "zh-Hant";
} else if (language_isocode == "zh_CN") {
/* Simplified Chinese */
lang = "zh-Hans";
} else {
/* Just copy the first part of the isocode. */
lang = language_isocode.substr(0, language_isocode.find('_'));
}
/* Create a font descriptor matching the wanted language and latin (english) glyphs.
* Can't use CFAutoRelease here for everything due to the way the dictionary has to be created. */
CFStringRef lang_codes[2];
lang_codes[0] = CFStringCreateWithCString(kCFAllocatorDefault, lang.c_str(), kCFStringEncodingUTF8);
lang_codes[1] = CFSTR("en");
CFArrayRef lang_arr = CFArrayCreate(kCFAllocatorDefault, (const void **)lang_codes, lengthof(lang_codes), &kCFTypeArrayCallBacks);
CFAutoRelease<CFDictionaryRef> lang_attribs(CFDictionaryCreate(kCFAllocatorDefault, const_cast<const void **>(reinterpret_cast<const void *const *>(&kCTFontLanguagesAttribute)), (const void **)&lang_arr, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
CFAutoRelease<CTFontDescriptorRef> lang_desc(CTFontDescriptorCreateWithAttributes(lang_attribs.get()));
CFRelease(lang_arr);
CFRelease(lang_codes[0]);
/* Get array of all font descriptors for the wanted language. */
CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault, const_cast<const void **>(reinterpret_cast<const void *const *>(&kCTFontLanguagesAttribute)), 1, &kCFTypeSetCallBacks));
CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(lang_desc.get(), mandatory_attribs.get()));
bool result = false;
for (int tries = 0; tries < 2; tries++) {
for (CFIndex i = 0; descs.get() != nullptr && i < CFArrayGetCount(descs.get()); i++) {
CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), i);
/* Get font traits. */
CFAutoRelease<CFDictionaryRef> traits((CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute));
CTFontSymbolicTraits symbolic_traits;
CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits);
/* Skip symbol fonts and vertical fonts. */
if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait)) continue;
/* Skip bold fonts (especially Arial Bold, which looks worse than regular Arial). */
if (symbolic_traits & kCTFontBoldTrait) continue;
/* Select monospaced fonts if asked for. */
if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != callback->Monospace()) continue;
/* Get font name. */
char buffer[128];
CFAutoRelease<CFStringRef> font_name((CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontDisplayNameAttribute));
CFStringGetCString(font_name.get(), buffer, std::size(buffer), kCFStringEncodingUTF8);
/* Serif fonts usually look worse on-screen with only small
* font sizes. As such, we try for a sans-serif font first.
* If we can't find one in the first try, try all fonts. */
if (tries == 0 && (symbolic_traits & kCTFontClassMaskTrait) != (CTFontStylisticClass)kCTFontSansSerifClass) continue;
/* There are some special fonts starting with an '.' and the last
* resort font that aren't usable. Skip them. */
std::string_view name{buffer};
if (name.starts_with(".") || name.starts_with("LastResort")) continue;
/* Save result. */
callback->SetFontNames(settings, name);
if (!callback->FindMissingGlyphs()) {
Debug(fontcache, 2, "CT-Font for {}: {}", language_isocode, name);
result = true;
break;
}
}
}
if (!result) {
/* For some OS versions, the font 'Arial Unicode MS' does not report all languages it
* supports. If we didn't find any other font, just try it, maybe we get lucky. */
callback->SetFontNames(settings, "Arial Unicode MS");
result = !callback->FindMissingGlyphs();
}
callback->FindMissingGlyphs();
return result;
} }
new CoreTextFontCache(fs, std::move(font_ref), GetFontCacheFontSize(fs)); private:
} static CTFontDescriptorRef LoadFontFromFile(const std::string &font_name)
{
if (!MacOSVersionIsAtLeast(10, 6, 0)) return nullptr;
/* Might be a font file name, try load it. Direct font loading is
* only supported starting on OSX 10.6. */
CFAutoRelease<CFStringRef> path;
/* See if this is an absolute path. */
if (FileExists(font_name)) {
path.reset(CFStringCreateWithCString(kCFAllocatorDefault, font_name.c_str(), kCFStringEncodingUTF8));
} else {
/* Scan the search-paths to see if it can be found. */
std::string full_font = FioFindFullPath(BASE_DIR, font_name);
if (!full_font.empty()) {
path.reset(CFStringCreateWithCString(kCFAllocatorDefault, full_font.c_str(), kCFStringEncodingUTF8));
}
}
if (path) {
/* Try getting a font descriptor to see if the system can use it. */
CFAutoRelease<CFURLRef> url(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path.get(), kCFURLPOSIXPathStyle, false));
CFAutoRelease<CFArrayRef> descs(CTFontManagerCreateFontDescriptorsFromURL(url.get()));
if (descs && CFArrayGetCount(descs.get()) > 0) {
CTFontDescriptorRef font_ref = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), 0);
CFRetain(font_ref);
return font_ref;
}
}
return nullptr;
}
};
static CoreTextFontCacheFactory s_coretext_fontcache_Factory;

View File

@ -12,6 +12,7 @@ add_files(
add_files( add_files(
font_unix.cpp font_unix.cpp
font_unix.h
CONDITION Fontconfig_FOUND CONDITION Fontconfig_FOUND
) )

View File

@ -11,17 +11,17 @@
#include "../../misc/autorelease.hpp" #include "../../misc/autorelease.hpp"
#include "../../debug.h" #include "../../debug.h"
#include "../../fontdetection.h" #include "../../fontcache.h"
#include "../../string_func.h" #include "../../string_func.h"
#include "../../strings_func.h" #include "../../strings_func.h"
#include "font_unix.h"
#include <fontconfig/fontconfig.h> #include <fontconfig/fontconfig.h>
#include "../../safeguards.h"
#include <ft2build.h> #include <ft2build.h>
#include FT_FREETYPE_H #include FT_FREETYPE_H
#include "../../safeguards.h"
extern FT_Library _ft_library; extern FT_Library _ft_library;
/** /**
@ -61,6 +61,12 @@ static std::tuple<std::string, std::string> SplitFontFamilyAndStyle(std::string_
return { std::string(font_name.substr(0, separator)), std::string(font_name.substr(begin)) }; return { std::string(font_name.substr(0, separator)), std::string(font_name.substr(begin)) };
} }
/**
* Load a freetype font face with the given font name.
* @param font_name The name of the font to load.
* @param face The face that has been found.
* @return The error we encountered.
*/
FT_Error GetFontByFaceName(std::string_view font_name, FT_Face *face) FT_Error GetFontByFaceName(std::string_view font_name, FT_Face *face)
{ {
FT_Error err = FT_Err_Cannot_Open_Resource; FT_Error err = FT_Err_Cannot_Open_Resource;
@ -117,7 +123,7 @@ FT_Error GetFontByFaceName(std::string_view font_name, FT_Face *face)
return err; return err;
} }
bool SetFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback) bool FontConfigFindFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback)
{ {
bool ret = false; bool ret = false;
@ -182,6 +188,6 @@ bool SetFallbackFont(FontCacheSettings *settings, const std::string &language_is
if (best_font == nullptr) return false; if (best_font == nullptr) return false;
callback->SetFontNames(settings, best_font, &best_index); callback->SetFontNames(settings, best_font, &best_index);
InitFontCache(callback->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED); FontCache::LoadFontCaches(callback->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
return true; return true;
} }

View File

@ -0,0 +1,26 @@
/*
* This file is part of OpenTTD.
* OpenTTD 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, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file font_unix.h Functions related to detecting/finding the right font. */
#ifndef FONT_UNIX_H
#define FONT_UNIX_H
#ifdef WITH_FONTCONFIG
#include "../../fontcache.h"
#include <ft2build.h>
#include FT_FREETYPE_H
FT_Error GetFontByFaceName(std::string_view font_name, FT_Face *face);
bool FontConfigFindFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback);
#endif /* WITH_FONTCONFIG */
#endif /* FONT_UNIX_H */

View File

@ -15,7 +15,6 @@
#include "../../fileio_func.h" #include "../../fileio_func.h"
#include "../../fontcache.h" #include "../../fontcache.h"
#include "../../fontcache/truetypefontcache.h" #include "../../fontcache/truetypefontcache.h"
#include "../../fontdetection.h"
#include "../../library_loader.h" #include "../../library_loader.h"
#include "../../string_func.h" #include "../../string_func.h"
#include "../../strings_func.h" #include "../../strings_func.h"
@ -85,32 +84,6 @@ static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *logfont, const NEWTEXT
return 0; // stop enumerating return 0; // stop enumerating
} }
bool SetFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback)
{
Debug(fontcache, 1, "Trying fallback fonts");
EFCParam langInfo;
std::wstring lang = OTTD2FS(language_isocode.substr(0, language_isocode.find('_')));
if (GetLocaleInfoEx(lang.c_str(), LOCALE_FONTSIGNATURE, reinterpret_cast<LPWSTR>(&langInfo.locale), sizeof(langInfo.locale) / sizeof(wchar_t)) == 0) {
/* Invalid isocode or some other mysterious error, can't determine fallback font. */
Debug(fontcache, 1, "Can't get locale info for fallback font (isocode={})", language_isocode);
return false;
}
langInfo.settings = settings;
langInfo.callback = callback;
LOGFONT font;
/* Enumerate all fonts. */
font.lfCharSet = DEFAULT_CHARSET;
font.lfFaceName[0] = '\0';
font.lfPitchAndFamily = 0;
HDC dc = GetDC(nullptr);
int ret = EnumFontFamiliesEx(dc, &font, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&langInfo, 0);
ReleaseDC(nullptr, dc);
return ret == 0;
}
/** /**
* Create a new Win32FontCache. * Create a new Win32FontCache.
* @param fs The font size that is going to be cached. * @param fs The font size that is going to be cached.
@ -293,99 +266,134 @@ void Win32FontCache::ClearFontCache()
return allow_fallback && key >= SCC_SPRITE_START && key <= SCC_SPRITE_END ? this->parent->MapCharToGlyph(key) : 0; return allow_fallback && key >= SCC_SPRITE_START && key <= SCC_SPRITE_END ? this->parent->MapCharToGlyph(key) : 0;
} }
class Win32FontCacheFactory : FontCacheFactory {
public:
Win32FontCacheFactory() : FontCacheFactory("win32", "Win32 font loader") {}
static bool TryLoadFontFromFile(const std::string &font_name, LOGFONT &logfont) /**
{ * Loads the GDI font.
wchar_t fontPath[MAX_PATH] = {}; * If a GDI font description is present, e.g. from the automatic font
* fallback search, use it. Otherwise, try to resolve it by font name.
* @param fs The font size to load.
*/
std::unique_ptr<FontCache> LoadFont(FontSize fs, FontType fonttype) override
{
if (fonttype != FontType::TrueType) return nullptr;
/* See if this is an absolute path. */ FontCacheSubSetting *settings = GetFontCacheSubSetting(fs);
if (FileExists(font_name)) {
convert_to_fs(font_name, fontPath); std::string font = GetFontCacheFontName(fs);
} else { if (font.empty()) return nullptr;
/* Scan the search-paths to see if it can be found. */
std::string full_font = FioFindFullPath(BASE_DIR, font_name); LOGFONT logfont{};
if (!full_font.empty()) { logfont.lfPitchAndFamily = fs == FS_MONO ? FIXED_PITCH : VARIABLE_PITCH;
convert_to_fs(font_name, fontPath); logfont.lfCharSet = DEFAULT_CHARSET;
logfont.lfOutPrecision = OUT_OUTLINE_PRECIS;
logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
if (settings->os_handle != nullptr) {
logfont = *(const LOGFONT *)settings->os_handle;
} else if (font.find('.') != std::string::npos) {
/* Might be a font file name, try load it. */
if (!TryLoadFontFromFile(font, logfont)) {
ShowInfo("Unable to load file '{}' for {} font, using default windows font selection instead", font, FontSizeToName(fs));
}
} }
if (logfont.lfFaceName[0] == 0) {
logfont.lfWeight = StrContainsIgnoreCase(font, " bold") ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts.
convert_to_fs(font, logfont.lfFaceName);
}
return LoadWin32Font(fs, logfont, GetFontCacheFontSize(fs), font);
} }
if (fontPath[0] != 0) { bool FindFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback) override
if (AddFontResourceEx(fontPath, FR_PRIVATE, 0) != 0) { {
/* Try a nice little undocumented function first for getting the internal font name. Debug(fontcache, 1, "Trying fallback fonts");
* Some documentation is found at: http://www.undocprint.org/winspool/getfontresourceinfo */ EFCParam langInfo;
static LibraryLoader _gdi32("gdi32.dll"); std::wstring lang = OTTD2FS(language_isocode.substr(0, language_isocode.find('_')));
typedef BOOL(WINAPI *PFNGETFONTRESOURCEINFO)(LPCTSTR, LPDWORD, LPVOID, DWORD); if (GetLocaleInfoEx(lang.c_str(), LOCALE_FONTSIGNATURE, reinterpret_cast<LPWSTR>(&langInfo.locale), sizeof(langInfo.locale) / sizeof(wchar_t)) == 0) {
static PFNGETFONTRESOURCEINFO GetFontResourceInfo = _gdi32.GetFunction("GetFontResourceInfoW"); /* Invalid isocode or some other mysterious error, can't determine fallback font. */
Debug(fontcache, 1, "Can't get locale info for fallback font (isocode={})", language_isocode);
return false;
}
langInfo.settings = settings;
langInfo.callback = callback;
if (GetFontResourceInfo != nullptr) { LOGFONT font;
/* Try to query an array of LOGFONTs that describe the file. */ /* Enumerate all fonts. */
DWORD len = 0; font.lfCharSet = DEFAULT_CHARSET;
if (GetFontResourceInfo(fontPath, &len, nullptr, 2) && len >= sizeof(LOGFONT)) { font.lfFaceName[0] = '\0';
LOGFONT *buf = (LOGFONT *)new uint8_t[len]; font.lfPitchAndFamily = 0;
if (GetFontResourceInfo(fontPath, &len, buf, 2)) {
logfont = *buf; // Just use first entry. HDC dc = GetDC(nullptr);
int ret = EnumFontFamiliesEx(dc, &font, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&langInfo, 0);
ReleaseDC(nullptr, dc);
return ret == 0;
}
private:
static std::unique_ptr<FontCache> LoadWin32Font(FontSize fs, const LOGFONT &logfont, uint size, std::string_view font_name)
{
HFONT font = CreateFontIndirect(&logfont);
if (font == nullptr) {
ShowInfo("Unable to use '{}' for {} font, Win32 reported error 0x{:X}, using sprite font instead", font_name, FontSizeToName(fs), GetLastError());
return nullptr;
}
DeleteObject(font);
return std::make_unique<Win32FontCache>(fs, logfont, size);
}
static bool TryLoadFontFromFile(const std::string &font_name, LOGFONT &logfont)
{
wchar_t fontPath[MAX_PATH] = {};
/* See if this is an absolute path. */
if (FileExists(font_name)) {
convert_to_fs(font_name, fontPath);
} else {
/* Scan the search-paths to see if it can be found. */
std::string full_font = FioFindFullPath(BASE_DIR, font_name);
if (!full_font.empty()) {
convert_to_fs(font_name, fontPath);
}
}
if (fontPath[0] != 0) {
if (AddFontResourceEx(fontPath, FR_PRIVATE, 0) != 0) {
/* Try a nice little undocumented function first for getting the internal font name.
* Some documentation is found at: http://www.undocprint.org/winspool/getfontresourceinfo */
static LibraryLoader _gdi32("gdi32.dll");
typedef BOOL(WINAPI *PFNGETFONTRESOURCEINFO)(LPCTSTR, LPDWORD, LPVOID, DWORD);
static PFNGETFONTRESOURCEINFO GetFontResourceInfo = _gdi32.GetFunction("GetFontResourceInfoW");
if (GetFontResourceInfo != nullptr) {
/* Try to query an array of LOGFONTs that describe the file. */
DWORD len = 0;
if (GetFontResourceInfo(fontPath, &len, nullptr, 2) && len >= sizeof(LOGFONT)) {
LOGFONT *buf = (LOGFONT *)new uint8_t[len];
if (GetFontResourceInfo(fontPath, &len, buf, 2)) {
logfont = *buf; // Just use first entry.
}
delete[](uint8_t *)buf;
} }
delete[](uint8_t *)buf; }
/* No dice yet. Use the file name as the font face name, hoping it matches. */
if (logfont.lfFaceName[0] == 0) {
wchar_t fname[_MAX_FNAME];
_wsplitpath(fontPath, nullptr, nullptr, fname, nullptr);
wcsncpy_s(logfont.lfFaceName, lengthof(logfont.lfFaceName), fname, _TRUNCATE);
logfont.lfWeight = StrContainsIgnoreCase(font_name, " bold") || StrContainsIgnoreCase(font_name, "-bold") ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts.
} }
} }
/* No dice yet. Use the file name as the font face name, hoping it matches. */
if (logfont.lfFaceName[0] == 0) {
wchar_t fname[_MAX_FNAME];
_wsplitpath(fontPath, nullptr, nullptr, fname, nullptr);
wcsncpy_s(logfont.lfFaceName, lengthof(logfont.lfFaceName), fname, _TRUNCATE);
logfont.lfWeight = StrContainsIgnoreCase(font_name, " bold") || StrContainsIgnoreCase(font_name, "-bold") ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts.
}
} }
return logfont.lfFaceName[0] != 0;
} }
};
return logfont.lfFaceName[0] != 0; static Win32FontCacheFactory s_win32_fontcache_factory;
}
static void LoadWin32Font(FontSize fs, const LOGFONT &logfont, uint size, std::string_view font_name)
{
HFONT font = CreateFontIndirect(&logfont);
if (font == nullptr) {
ShowInfo("Unable to use '{}' for {} font, Win32 reported error 0x{:X}, using sprite font instead", font_name, FontSizeToName(fs), GetLastError());
return;
}
DeleteObject(font);
new Win32FontCache(fs, logfont, size);
}
/**
* Loads the GDI font.
* If a GDI font description is present, e.g. from the automatic font
* fallback search, use it. Otherwise, try to resolve it by font name.
* @param fs The font size to load.
*/
void LoadWin32Font(FontSize fs)
{
FontCacheSubSetting *settings = GetFontCacheSubSetting(fs);
std::string font = GetFontCacheFontName(fs);
if (font.empty()) return;
LOGFONT logfont{};
logfont.lfPitchAndFamily = fs == FS_MONO ? FIXED_PITCH : VARIABLE_PITCH;
logfont.lfCharSet = DEFAULT_CHARSET;
logfont.lfOutPrecision = OUT_OUTLINE_PRECIS;
logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
if (settings->os_handle != nullptr) {
logfont = *(const LOGFONT *)settings->os_handle;
} else if (font.find('.') != std::string::npos) {
/* Might be a font file name, try load it. */
if (!TryLoadFontFromFile(font, logfont)) {
ShowInfo("Unable to load file '{}' for {} font, using default windows font selection instead", font, FontSizeToName(fs));
}
}
if (logfont.lfFaceName[0] == 0) {
logfont.lfWeight = StrContainsIgnoreCase(font, " bold") ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts.
convert_to_fs(font, logfont.lfFaceName);
}
LoadWin32Font(fs, logfont, GetFontCacheFontSize(fs), font);
}

View File

@ -1036,8 +1036,8 @@ struct GameOptionsWindow : Window {
this->SetWidgetDisabledState(WID_GO_GUI_FONT_AA, _fcsettings.prefer_sprite); this->SetWidgetDisabledState(WID_GO_GUI_FONT_AA, _fcsettings.prefer_sprite);
this->SetDirty(); this->SetDirty();
InitFontCache(FONTSIZES_ALL); FontCache::LoadFontCaches(FONTSIZES_ALL);
ClearFontCache(FONTSIZES_ALL); FontCache::ClearFontCaches(FONTSIZES_ALL);
CheckForMissingGlyphs(); CheckForMissingGlyphs();
SetupWidgetDimensions(); SetupWidgetDimensions();
UpdateAllVirtCoords(); UpdateAllVirtCoords();
@ -1050,7 +1050,7 @@ struct GameOptionsWindow : Window {
this->SetWidgetLoweredState(WID_GO_GUI_FONT_AA, _fcsettings.global_aa); this->SetWidgetLoweredState(WID_GO_GUI_FONT_AA, _fcsettings.global_aa);
MarkWholeScreenDirty(); MarkWholeScreenDirty();
ClearFontCache(FONTSIZES_ALL); FontCache::ClearFontCaches(FONTSIZES_ALL);
break; break;
#endif /* HAS_TRUETYPE_FONT */ #endif /* HAS_TRUETYPE_FONT */

View File

@ -17,7 +17,6 @@
#include "newgrf_text.h" #include "newgrf_text.h"
#include "fileio_func.h" #include "fileio_func.h"
#include "signs_base.h" #include "signs_base.h"
#include "fontdetection.h"
#include "error.h" #include "error.h"
#include "error_func.h" #include "error_func.h"
#include "strings_func.h" #include "strings_func.h"
@ -2278,7 +2277,7 @@ std::string_view GetCurrentLanguageIsoCode()
*/ */
bool MissingGlyphSearcher::FindMissingGlyphs() bool MissingGlyphSearcher::FindMissingGlyphs()
{ {
InitFontCache(this->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED); FontCache::LoadFontCaches(this->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
this->Reset(); this->Reset();
for (auto text = this->NextString(); text.has_value(); text = this->NextString()) { for (auto text = this->NextString(); text.has_value(); text = this->NextString()) {
@ -2376,7 +2375,7 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
_fcsettings.mono.os_handle = nullptr; _fcsettings.mono.os_handle = nullptr;
_fcsettings.medium.os_handle = nullptr; _fcsettings.medium.os_handle = nullptr;
bad_font = !SetFallbackFont(&_fcsettings, _langpack.langpack->isocode, searcher); bad_font = !FontProviderManager::FindFallbackFont(&_fcsettings, _langpack.langpack->isocode, searcher);
_fcsettings = std::move(backup); _fcsettings = std::move(backup);
@ -2395,7 +2394,7 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
/* Our fallback font does miss characters too, so keep the /* Our fallback font does miss characters too, so keep the
* user chosen font as that is more likely to be any good than * user chosen font as that is more likely to be any good than
* the wild guess we made */ * the wild guess we made */
InitFontCache(searcher->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED); FontCache::LoadFontCaches(searcher->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
} }
} }
#endif #endif

View File

@ -36,10 +36,15 @@
# define MW(a) {a, PALETTE_TO_STRUCT_WHITE} # define MW(a) {a, PALETTE_TO_STRUCT_WHITE}
# define MC(a) {a, PALETTE_TO_STRUCT_CONCRETE} # define MC(a) {a, PALETTE_TO_STRUCT_CONCRETE}
static const PalSpriteID _aqueduct_sprites[] = { /* Sprite table for middle part of aqueduct. */
{ SPR_AQUEDUCT_MIDDLE_X, PAL_NONE }, { 0x0, PAL_NONE }, { SPR_AQUEDUCT_PILLAR_X, PAL_NONE }, { 0x0, PAL_NONE }, static const PalSpriteID _aqueduct_sprite_table_middle[] = {
{ SPR_AQUEDUCT_MIDDLE_Y, PAL_NONE }, { 0x0, PAL_NONE }, { SPR_AQUEDUCT_PILLAR_Y, PAL_NONE }, { 0x0, PAL_NONE }, {SPR_AQUEDUCT_MIDDLE_X, PAL_NONE}, {0x0, PAL_NONE}, {SPR_AQUEDUCT_PILLAR_X, PAL_NONE}, {0x0, PAL_NONE}, // AXIS_X
{ SPR_AQUEDUCT_RAMP_SW, PAL_NONE }, { SPR_AQUEDUCT_RAMP_SE, PAL_NONE }, { SPR_AQUEDUCT_RAMP_NE, PAL_NONE }, { SPR_AQUEDUCT_RAMP_NW, PAL_NONE }, {SPR_AQUEDUCT_MIDDLE_Y, PAL_NONE}, {0x0, PAL_NONE}, {SPR_AQUEDUCT_PILLAR_Y, PAL_NONE}, {0x0, PAL_NONE}, // AIXS_Y
};
/* Sprite table for head part of aqueduct. */
static const PalSpriteID _aqueduct_sprite_table_heads[] = {
{SPR_AQUEDUCT_RAMP_SW, PAL_NONE}, {SPR_AQUEDUCT_RAMP_SE, PAL_NONE}, {SPR_AQUEDUCT_RAMP_NE, PAL_NONE}, {SPR_AQUEDUCT_RAMP_NW, PAL_NONE},
}; };
static const PalSpriteID _bridge_sprite_table_4_0[] = { static const PalSpriteID _bridge_sprite_table_4_0[] = {

View File

@ -32,7 +32,8 @@ public:
static void InitializeFontCaches() static void InitializeFontCaches()
{ {
for (FontSize fs = FS_BEGIN; fs != FS_END; fs++) { for (FontSize fs = FS_BEGIN; fs != FS_END; fs++) {
if (FontCache::caches[fs] == nullptr) new MockFontCache(fs); /* FontCache inserts itself into to the cache. */ if (FontCache::Get(fs) != nullptr) continue;
FontCache::Register(std::make_unique<MockFontCache>(fs));
} }
} }
}; };

View File

@ -138,15 +138,58 @@ bool HasBridgeFlatRamp(Slope tileh, Axis axis)
return (tileh != SLOPE_FLAT); return (tileh != SLOPE_FLAT);
} }
static inline std::span<const PalSpriteID> GetBridgeSpriteTable(int index, BridgePieces table) /**
* Get the sprite table for a rail/road bridge piece.
* @param bridge_type Bridge type.
* @param piece Bridge piece.
* @return Sprite table for the bridge piece.
*/
static std::span<const PalSpriteID> GetBridgeSpriteTable(BridgeType bridge_type, BridgePieces piece)
{ {
const BridgeSpec *bridge = GetBridgeSpec(index); assert(piece < NUM_BRIDGE_PIECES);
assert(table < NUM_BRIDGE_PIECES);
if (table < bridge->sprite_table.size() && !bridge->sprite_table[table].empty()) return bridge->sprite_table[table];
return _bridge_sprite_table[index][table]; const BridgeSpec *bridge = GetBridgeSpec(bridge_type);
if (piece < bridge->sprite_table.size() && !bridge->sprite_table[piece].empty()) return bridge->sprite_table[piece];
return _bridge_sprite_table[bridge_type][piece];
} }
/**
* Get the sprite table transport type base offset for a rail/road bridge.
* @param transport_type Transport type of bridge.
* @param ramp Tile of bridge ramp.
* @return Offset for transport type.
*/
static uint8_t GetBridgeSpriteTableBaseOffset(TransportType transport_type, TileIndex ramp)
{
switch (transport_type) {
case TRANSPORT_RAIL: return GetRailTypeInfo(GetRailType(ramp))->bridge_offset;
case TRANSPORT_ROAD: return 8;
default: NOT_REACHED();
}
}
/**
* Get bridge sprite table base offset for the ramp part of bridge.
* @param diagdir Direction of ramp.
* @return Offset for direction.
*/
static uint8_t GetBridgeRampDirectionBaseOffset(DiagDirection diagdir)
{
/* Bridge ramps are ordered SW, SE, NE, NW instead of NE, SE, SW, NW. */
static constexpr uint8_t ramp_offsets[DIAGDIR_END] = {2, 1, 0, 3};
return ramp_offsets[diagdir];
}
/**
* Get bridge sprite table base offset for the middle part of bridge.
* @param axis Axis of bridge.
* @return Offset for axis.
*/
static uint8_t GetBridgeMiddleAxisBaseOffset(Axis axis)
{
return axis == AXIS_X ? 0 : 4;
}
/** /**
* Determines the foundation for the bridge head, and tests if the resulting slope is valid. * Determines the foundation for the bridge head, and tests if the resulting slope is valid.
@ -1017,10 +1060,10 @@ static CommandCost ClearTile_TunnelBridge(TileIndex tile, DoCommandFlags flags)
* @param h Bounding box size in Y direction * @param h Bounding box size in Y direction
* @param subsprite Optional subsprite for drawing halfpillars * @param subsprite Optional subsprite for drawing halfpillars
*/ */
static inline void DrawPillar(const PalSpriteID *psid, int x, int y, int z, uint8_t w, uint8_t h, const SubSprite *subsprite) static inline void DrawPillar(const PalSpriteID &psid, int x, int y, int z, uint8_t w, uint8_t h, const SubSprite *subsprite)
{ {
static const int PILLAR_Z_OFFSET = TILE_HEIGHT - BRIDGE_Z_START; ///< Start offset of pillar wrt. bridge (downwards) static const int PILLAR_Z_OFFSET = TILE_HEIGHT - BRIDGE_Z_START; ///< Start offset of pillar wrt. bridge (downwards)
AddSortableSpriteToDraw(psid->sprite, psid->pal, x, y, z, {{0, 0, -PILLAR_Z_OFFSET}, {w, h, BB_HEIGHT_UNDER_BRIDGE}, {0, 0, PILLAR_Z_OFFSET}}, IsTransparencySet(TO_BRIDGES), subsprite); AddSortableSpriteToDraw(psid.sprite, psid.pal, x, y, z, {{0, 0, -PILLAR_Z_OFFSET}, {w, h, BB_HEIGHT_UNDER_BRIDGE}, {0, 0, PILLAR_Z_OFFSET}}, IsTransparencySet(TO_BRIDGES), subsprite);
} }
/** /**
@ -1034,7 +1077,7 @@ static inline void DrawPillar(const PalSpriteID *psid, int x, int y, int z, uint
* @param h Bounding box size in Y direction * @param h Bounding box size in Y direction
* @return Reached Z at the bottom * @return Reached Z at the bottom
*/ */
static int DrawPillarColumn(int z_bottom, int z_top, const PalSpriteID *psid, int x, int y, int w, int h) static int DrawPillarColumn(int z_bottom, int z_top, const PalSpriteID &psid, int x, int y, int w, int h)
{ {
int cur_z; int cur_z;
for (cur_z = z_top; cur_z >= z_bottom; cur_z -= TILE_HEIGHT) { for (cur_z = z_top; cur_z >= z_bottom; cur_z -= TILE_HEIGHT) {
@ -1054,7 +1097,7 @@ static int DrawPillarColumn(int z_bottom, int z_top, const PalSpriteID *psid, in
* @param y Sprite Y position of front pillar. * @param y Sprite Y position of front pillar.
* @param z_bridge Absolute height of bridge bottom. * @param z_bridge Absolute height of bridge bottom.
*/ */
static void DrawBridgePillars(const PalSpriteID *psid, const TileInfo *ti, Axis axis, bool drawfarpillar, int x, int y, int z_bridge) static void DrawBridgePillars(const PalSpriteID &psid, const TileInfo *ti, Axis axis, bool drawfarpillar, int x, int y, int z_bridge)
{ {
static const int bounding_box_size[2] = {16, 2}; ///< bounding box size of pillars along bridge direction static const int bounding_box_size[2] = {16, 2}; ///< bounding box size of pillars along bridge direction
static const int back_pillar_offset[2] = { 0, 9}; ///< sprite position offset of back facing pillar static const int back_pillar_offset[2] = { 0, 9}; ///< sprite position offset of back facing pillar
@ -1065,7 +1108,7 @@ static void DrawBridgePillars(const PalSpriteID *psid, const TileInfo *ti, Axis
{ { -INF, -INF, 15, INF }, { 16, -INF, INF, INF } }, // Y axis, north and south { { -INF, -INF, 15, INF }, { 16, -INF, INF, INF } }, // Y axis, north and south
}; };
if (psid->sprite == 0) return; if (psid.sprite == 0) return;
/* Determine ground height under pillars */ /* Determine ground height under pillars */
DiagDirection south_dir = AxisToDiagDir(axis); DiagDirection south_dir = AxisToDiagDir(axis);
@ -1404,34 +1447,20 @@ static void DrawTile_TunnelBridge(TileInfo *ti)
DrawBridgeMiddle(ti); DrawBridgeMiddle(ti);
} else { // IsBridge(ti->tile) } else { // IsBridge(ti->tile)
const PalSpriteID *psid;
int base_offset;
bool ice = HasTunnelBridgeSnowOrDesert(ti->tile);
if (transport_type == TRANSPORT_RAIL) {
base_offset = GetRailTypeInfo(GetRailType(ti->tile))->bridge_offset;
assert(base_offset != 8); // This one is used for roads
} else {
base_offset = 8;
}
/* as the lower 3 bits are used for other stuff, make sure they are clear */
assert( (base_offset & 0x07) == 0x00);
DrawFoundation(ti, GetBridgeFoundation(ti->tileh, DiagDirToAxis(tunnelbridge_direction))); DrawFoundation(ti, GetBridgeFoundation(ti->tileh, DiagDirToAxis(tunnelbridge_direction)));
/* HACK Wizardry to convert the bridge ramp direction into a sprite offset */ uint base_offset = GetBridgeRampDirectionBaseOffset(tunnelbridge_direction);
base_offset += (6 - tunnelbridge_direction) % 4; std::span<const PalSpriteID> psid;
/* Table number BRIDGE_PIECE_HEAD always refers to the bridge heads for any bridge type */
if (transport_type != TRANSPORT_WATER) { if (transport_type != TRANSPORT_WATER) {
if (ti->tileh == SLOPE_FLAT) base_offset += 4; // sloped bridge head if (ti->tileh == SLOPE_FLAT) base_offset += 4; // sloped bridge head
psid = &GetBridgeSpriteTable(GetBridgeType(ti->tile), BRIDGE_PIECE_HEAD)[base_offset]; base_offset += GetBridgeSpriteTableBaseOffset(transport_type, ti->tile);
psid = GetBridgeSpriteTable(GetBridgeType(ti->tile), BRIDGE_PIECE_HEAD);
} else { } else {
psid = _aqueduct_sprites + base_offset; psid = _aqueduct_sprite_table_heads;
} }
psid = psid.subspan(base_offset, 1);
if (!ice) { if (!HasTunnelBridgeSnowOrDesert(ti->tile)) {
TileIndex next = ti->tile + TileOffsByDiagDir(tunnelbridge_direction); TileIndex next = ti->tile + TileOffsByDiagDir(tunnelbridge_direction);
if (ti->tileh != SLOPE_FLAT && ti->z == 0 && HasTileWaterClass(next) && GetWaterClass(next) == WATER_CLASS_SEA) { if (ti->tileh != SLOPE_FLAT && ti->z == 0 && HasTileWaterClass(next) && GetWaterClass(next) == WATER_CLASS_SEA) {
DrawShoreTile(ti->tileh); DrawShoreTile(ti->tileh);
@ -1451,7 +1480,7 @@ static void DrawTile_TunnelBridge(TileInfo *ti)
* it doesn't disappear behind it * it doesn't disappear behind it
*/ */
/* Bridge heads are drawn solid no matter how invisibility/transparency is set */ /* Bridge heads are drawn solid no matter how invisibility/transparency is set */
AddSortableSpriteToDraw(psid->sprite, psid->pal, *ti, {{}, {TILE_SIZE, TILE_SIZE, static_cast<uint8_t>(ti->tileh == SLOPE_FLAT ? 0 : TILE_HEIGHT)}, {}}); AddSortableSpriteToDraw(psid[0].sprite, psid[0].pal, *ti, {{}, {TILE_SIZE, TILE_SIZE, static_cast<uint8_t>(ti->tileh == SLOPE_FLAT ? 0 : TILE_HEIGHT)}, {}});
if (transport_type == TRANSPORT_ROAD) { if (transport_type == TRANSPORT_ROAD) {
uint offset = tunnelbridge_direction; uint offset = tunnelbridge_direction;
@ -1572,33 +1601,21 @@ void DrawBridgeMiddle(const TileInfo *ti)
TileIndex rampnorth = GetNorthernBridgeEnd(ti->tile); TileIndex rampnorth = GetNorthernBridgeEnd(ti->tile);
TileIndex rampsouth = GetSouthernBridgeEnd(ti->tile); TileIndex rampsouth = GetSouthernBridgeEnd(ti->tile);
TransportType transport_type = GetTunnelBridgeTransportType(rampsouth); TransportType transport_type = GetTunnelBridgeTransportType(rampsouth);
Axis axis = GetBridgeAxis(ti->tile); Axis axis = GetBridgeAxis(ti->tile);
BridgePieces piece = CalcBridgePiece(
GetTunnelBridgeLength(ti->tile, rampnorth) + 1,
GetTunnelBridgeLength(ti->tile, rampsouth) + 1
);
const PalSpriteID *psid; uint base_offset = GetBridgeMiddleAxisBaseOffset(axis);
std::span<const PalSpriteID> psid;
bool drawfarpillar; bool drawfarpillar;
if (transport_type != TRANSPORT_WATER) { if (transport_type != TRANSPORT_WATER) {
BridgeType type = GetBridgeType(rampsouth); BridgeType bridge_type = GetBridgeType(rampsouth);
drawfarpillar = !HasBit(GetBridgeSpec(type)->flags, 0); drawfarpillar = !HasBit(GetBridgeSpec(bridge_type)->flags, 0);
base_offset += GetBridgeSpriteTableBaseOffset(transport_type, rampsouth);
uint base_offset; psid = GetBridgeSpriteTable(bridge_type, CalcBridgePiece(GetTunnelBridgeLength(ti->tile, rampnorth) + 1, GetTunnelBridgeLength(ti->tile, rampsouth) + 1));
if (transport_type == TRANSPORT_RAIL) {
base_offset = GetRailTypeInfo(GetRailType(rampsouth))->bridge_offset;
} else {
base_offset = 8;
}
psid = &GetBridgeSpriteTable(type, piece)[base_offset];
} else { } else {
drawfarpillar = true; drawfarpillar = true;
psid = _aqueduct_sprites; psid = _aqueduct_sprite_table_middle;
} }
psid = psid.subspan(base_offset, 3);
if (axis != AXIS_X) psid += 4;
int x = ti->x; int x = ti->x;
int y = ti->y; int y = ti->y;
@ -1614,14 +1631,12 @@ void DrawBridgeMiddle(const TileInfo *ti)
/* Draw floor and far part of bridge*/ /* Draw floor and far part of bridge*/
if (!IsInvisibilitySet(TO_BRIDGES)) { if (!IsInvisibilitySet(TO_BRIDGES)) {
if (axis == AXIS_X) { if (axis == AXIS_X) {
AddSortableSpriteToDraw(psid->sprite, psid->pal, x, y, z, {{0, 0, BRIDGE_Z_START}, {TILE_SIZE, 1, 40}, {0, 0, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES)); AddSortableSpriteToDraw(psid[0].sprite, psid[0].pal, x, y, z, {{0, 0, BRIDGE_Z_START}, {TILE_SIZE, 1, 40}, {0, 0, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES));
} else { } else {
AddSortableSpriteToDraw(psid->sprite, psid->pal, x, y, z, {{0, 0, BRIDGE_Z_START}, {1, TILE_SIZE, 40}, {0, 0, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES)); AddSortableSpriteToDraw(psid[0].sprite, psid[0].pal, x, y, z, {{0, 0, BRIDGE_Z_START}, {1, TILE_SIZE, 40}, {0, 0, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES));
} }
} }
psid++;
if (transport_type == TRANSPORT_ROAD) { if (transport_type == TRANSPORT_ROAD) {
/* DrawBridgeRoadBits() calls EndSpriteCombine() and StartSpriteCombine() */ /* DrawBridgeRoadBits() calls EndSpriteCombine() and StartSpriteCombine() */
DrawBridgeRoadBits(rampsouth, x, y, bridge_z, axis ^ 1, false); DrawBridgeRoadBits(rampsouth, x, y, bridge_z, axis ^ 1, false);
@ -1654,10 +1669,10 @@ void DrawBridgeMiddle(const TileInfo *ti)
if (!IsInvisibilitySet(TO_BRIDGES)) { if (!IsInvisibilitySet(TO_BRIDGES)) {
if (axis == AXIS_X) { if (axis == AXIS_X) {
y += 12; y += 12;
if (psid->sprite & SPRITE_MASK) AddSortableSpriteToDraw(psid->sprite, psid->pal, x, y, z, {{0, 3, BRIDGE_Z_START}, {TILE_SIZE, 1, 40}, {0, -3, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES)); if (psid[1].sprite & SPRITE_MASK) AddSortableSpriteToDraw(psid[1].sprite, psid[1].pal, x, y, z, {{0, 3, BRIDGE_Z_START}, {TILE_SIZE, 1, 40}, {0, -3, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES));
} else { } else {
x += 12; x += 12;
if (psid->sprite & SPRITE_MASK) AddSortableSpriteToDraw(psid->sprite, psid->pal, x, y, z, {{3, 0, BRIDGE_Z_START}, {1, TILE_SIZE, 40}, {-3, 0, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES)); if (psid[1].sprite & SPRITE_MASK) AddSortableSpriteToDraw(psid[1].sprite, psid[1].pal, x, y, z, {{3, 0, BRIDGE_Z_START}, {1, TILE_SIZE, 40}, {-3, 0, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES));
} }
} }
@ -1667,8 +1682,7 @@ void DrawBridgeMiddle(const TileInfo *ti)
/* Do not draw anything more if bridges are invisible */ /* Do not draw anything more if bridges are invisible */
if (IsInvisibilitySet(TO_BRIDGES)) return; if (IsInvisibilitySet(TO_BRIDGES)) return;
psid++; DrawBridgePillars(psid[2], ti, axis, drawfarpillar, x, y, z);
DrawBridgePillars(psid, ti, axis, drawfarpillar, x, y, z);
} }