OpenTTD Source: src/fontcache/freetypefontcache.cpp Source File

OpenTTD Source 15.3

freetypefontcache.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
10#ifdef WITH_FREETYPE
11
12#include "../stdafx.h"
13
14#include "../debug.h"
15#include "../fontcache.h"
16#include "../blitter/factory.hpp"
17#include "../zoom_func.h"
18#include "../fileio_func.h"
19#include "../error_func.h"
20#include "../../os/unix/font_unix.h"
21#include "truetypefontcache.h"
22
23#include "../table/control_codes.h"
24
25#include <ft2build.h>
26#include FT_FREETYPE_H
27#include FT_GLYPH_H
28#include FT_TRUETYPE_TABLES_H
29
30#include "../safeguards.h"
31
33 class FreeTypeFontCache : public TrueTypeFontCache {
34private:
35 FT_Face face;
36
37 void SetFontSize(int pixels);
38 const Sprite *InternalGetGlyph(GlyphID key, bool aa) override;
39
40public:
41 FreeTypeFontCache(FontSize fs, FT_Face face, int pixels);
42 ~FreeTypeFontCache();
43 void ClearFontCache() override;
44 GlyphID MapCharToGlyph(char32_t key, bool allow_fallback = true) override;
45 std::string GetFontName() override { return fmt::format("{}, {}", face->family_name, face->style_name); }
46 bool IsBuiltInFont() override { return false; }
47 const void *GetOSHandle() override { return &face; }
48};
49
56FreeTypeFontCache::FreeTypeFontCache(FontSize fs, FT_Face face, int pixels) : TrueTypeFontCache(fs, pixels), face(face)
57{
58 assert(face != nullptr);
59
60 this->SetFontSize(pixels);
61}
62
63void FreeTypeFontCache::SetFontSize(int pixels)
64{
65 if (pixels == 0) {
66 /* Try to determine a good height based on the minimal height recommended by the font. */
67 int scaled_height = ScaleGUITrad(FontCache::GetDefaultFontHeight(this->fs));
68 pixels = scaled_height;
69
70 TT_Header *head = (TT_Header *)FT_Get_Sfnt_Table(this->face, ft_sfnt_head);
71 if (head != nullptr) {
72 /* Font height is minimum height plus the difference between the default
73 * height for this font size and the small size. */
74 int diff = scaled_height - ScaleGUITrad(FontCache::GetDefaultFontHeight(FS_SMALL));
75 /* Clamp() is not used as scaled_height could be greater than MAX_FONT_SIZE, which is not permitted in Clamp(). */
76 pixels = std::min(std::max(std::min<int>(head->Lowest_Rec_PPEM, MAX_FONT_MIN_REC_SIZE) + diff, scaled_height), MAX_FONT_SIZE);
77 }
78 } else {
79 pixels = ScaleGUITrad(pixels);
80 }
81 this->used_size = pixels;
82
83 FT_Error err = FT_Set_Pixel_Sizes(this->face, 0, pixels);
84 if (err != FT_Err_Ok) {
85
86 /* Find nearest size to that requested */
87 FT_Bitmap_Size *bs = this->face->available_sizes;
88 int i = this->face->num_fixed_sizes;
89 if (i > 0) { // In pathetic cases one might get no fixed sizes at all.
90 int n = bs->height;
91 FT_Int chosen = 0;
92 for (; --i; bs++) {
93 if (abs(pixels - bs->height) >= abs(pixels - n)) continue;
94 n = bs->height;
95 chosen = this->face->num_fixed_sizes - i;
96 }
97
98 /* Don't use FT_Set_Pixel_Sizes here - it might give us another
99 * error, even though the size is available (FS#5885). */
100 err = FT_Select_Size(this->face, chosen);
101 }
102 }
103
104 if (err == FT_Err_Ok) {
105 this->ascender = this->face->size->metrics.ascender >> 6;
106 this->descender = this->face->size->metrics.descender >> 6;
107 this->height = this->ascender - this->descender;
108 } else {
109 /* Both FT_Set_Pixel_Sizes and FT_Select_Size failed. */
110 Debug(fontcache, 0, "Font size selection failed. Using FontCache defaults.");
111 }
112}
113
117FreeTypeFontCache::~FreeTypeFontCache()
118{
119 FT_Done_Face(this->face);
120 this->face = nullptr;
121 this->ClearFontCache();
122}
123
127 void FreeTypeFontCache::ClearFontCache()
128{
129 /* Font scaling might have changed, determine font size anew if it was automatically selected. */
130 if (this->face != nullptr) this->SetFontSize(this->req_size);
131
132 this->TrueTypeFontCache::ClearFontCache();
133}
134
135
136const Sprite *FreeTypeFontCache::InternalGetGlyph(GlyphID key, bool aa)
137{
138 FT_GlyphSlot slot = this->face->glyph;
139
140 FT_Load_Glyph(this->face, key, aa ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO);
141 FT_Render_Glyph(this->face->glyph, aa ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
142
143 /* Despite requesting a normal glyph, FreeType may have returned a bitmap */
144 aa = (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY);
145
146 /* Add 1 scaled pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel */
147 uint shadow = (this->fs == FS_NORMAL) ? ScaleGUITrad(1) : 0;
148 uint width = std::max(1U, (uint)slot->bitmap.width + shadow);
149 uint height = std::max(1U, (uint)slot->bitmap.rows + shadow);
150
151 /* Limit glyph size to prevent overflows later on. */
152 if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) UserError("Font glyph is too large");
153
154 /* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it */
155 SpriteLoader::SpriteCollection spritecollection;
156 SpriteLoader::Sprite &sprite = spritecollection[ZoomLevel::Min];
157 sprite.AllocateData(ZoomLevel::Min, static_cast<size_t>(width) * height);
158 sprite.colours = SpriteComponent::Palette;
159 if (aa) sprite.colours.Set(SpriteComponent::Alpha);
160 sprite.width = width;
161 sprite.height = height;
162 sprite.x_offs = slot->bitmap_left;
163 sprite.y_offs = this->ascender - slot->bitmap_top;
164
165 /* Draw shadow for medium size */
166 if (this->fs == FS_NORMAL && !aa) {
167 for (uint y = 0; y < (uint)slot->bitmap.rows; y++) {
168 for (uint x = 0; x < (uint)slot->bitmap.width; x++) {
169 if (HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
170 sprite.data[shadow + x + (shadow + y) * sprite.width].m = SHADOW_COLOUR;
171 sprite.data[shadow + x + (shadow + y) * sprite.width].a = 0xFF;
172 }
173 }
174 }
175 }
176
177 for (uint y = 0; y < (uint)slot->bitmap.rows; y++) {
178 for (uint x = 0; x < (uint)slot->bitmap.width; x++) {
179 if (aa ? (slot->bitmap.buffer[x + y * slot->bitmap.pitch] > 0) : HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
180 sprite.data[x + y * sprite.width].m = FACE_COLOUR;
181 sprite.data[x + y * sprite.width].a = aa ? slot->bitmap.buffer[x + y * slot->bitmap.pitch] : 0xFF;
182 }
183 }
184 }
185
186 UniquePtrSpriteAllocator allocator;
187 BlitterFactory::GetCurrentBlitter()->Encode(SpriteType::Font, spritecollection, allocator);
188
189 GlyphEntry new_glyph;
190 new_glyph.data = std::move(allocator.data);
191 new_glyph.width = slot->advance.x >> 6;
192
193 return this->SetGlyphPtr(key, std::move(new_glyph)).GetSprite();
194}
195
196
197 GlyphID FreeTypeFontCache::MapCharToGlyph(char32_t key, bool allow_fallback)
198{
199 assert(IsPrintable(key));
200
201 FT_UInt glyph = FT_Get_Char_Index(this->face, key);
202
203 if (glyph == 0 && allow_fallback && key >= SCC_SPRITE_START && key <= SCC_SPRITE_END) {
204 return this->parent->MapCharToGlyph(key);
205 }
206
207 return glyph;
208}
209
210FT_Library _ft_library = nullptr;
211
212 class FreeTypeFontCacheFactory : public FontCacheFactory {
213public:
214 FreeTypeFontCacheFactory() : FontCacheFactory("freetype", "FreeType font provider") {}
215
216 virtual ~FreeTypeFontCacheFactory()
217 {
218 FT_Done_FreeType(_ft_library);
219 _ft_library = nullptr;
220 }
221
229 std::unique_ptr<FontCache> LoadFont(FontSize fs, FontType fonttype) const override
230 {
231 if (fonttype != FontType::TrueType) return nullptr;
232
233 FontCacheSubSetting *settings = GetFontCacheSubSetting(fs);
234
235 std::string font = GetFontCacheFontName(fs);
236 if (font.empty()) return nullptr;
237
238 if (_ft_library == nullptr) {
239 if (FT_Init_FreeType(&_ft_library) != FT_Err_Ok) {
240 ShowInfo("Unable to initialize FreeType, using sprite fonts instead");
241 return nullptr;
242 }
243
244 Debug(fontcache, 2, "Initialized");
245 }
246
247 FT_Face face = nullptr;
248
249 /* If font is an absolute path to a ttf, try loading that first. */
250 int32_t index = 0;
251 if (settings->os_handle != nullptr) index = *static_cast<const int32_t *>(settings->os_handle);
252 FT_Error error = FT_New_Face(_ft_library, font.c_str(), index, &face);
253
254 if (error != FT_Err_Ok) {
255 /* Check if font is a relative filename in one of our search-paths. */
256 std::string full_font = FioFindFullPath(BASE_DIR, font);
257 if (!full_font.empty()) {
258 error = FT_New_Face(_ft_library, full_font.c_str(), 0, &face);
259 }
260 }
261
262#ifdef WITH_FONTCONFIG
263 /* Try loading based on font face name (OS-wide fonts). */
264 if (error != FT_Err_Ok) error = GetFontByFaceName(font, &face);
265#endif /* WITH_FONTCONFIG */
266
267 if (error != FT_Err_Ok) {
268 FT_Done_Face(face);
269 return nullptr;
270 }
271
272 return LoadFont(fs, face, font, GetFontCacheFontSize(fs));
273 }
274
275 bool FindFallbackFont(struct FontCacheSettings *settings, const std::string &language_isocode, class MissingGlyphSearcher *callback) const override
276 {
277#ifdef WITH_FONTCONFIG
278 if (FontConfigFindFallbackFont(settings, language_isocode, callback)) return true;
279#endif /* WITH_FONTCONFIG */
280
281 return false;
282 }
283
284private:
285 static std::unique_ptr<FontCache> LoadFont(FontSize fs, FT_Face face, std::string_view font_name, uint size)
286 {
287 Debug(fontcache, 2, "Requested '{}', using '{} {}'", font_name, face->family_name, face->style_name);
288
289 /* Attempt to select the unicode character map */
290 FT_Error error = FT_Select_Charmap(face, ft_encoding_unicode);
291 if (error == FT_Err_Invalid_CharMap_Handle) {
292 /* Try to pick a different character map instead. We default to
293 * the first map, but platform_id 0 encoding_id 0 should also
294 * be unicode (strange system...) */
295 FT_CharMap found = face->charmaps[0];
296
297 for (int i = 0; i < face->num_charmaps; ++i) {
298 FT_CharMap charmap = face->charmaps[i];
299 if (charmap->platform_id == 0 && charmap->encoding_id == 0) {
300 found = charmap;
301 }
302 }
303
304 if (found != nullptr) {
305 error = FT_Set_Charmap(face, found);
306 }
307 }
308
309 if (error != FT_Err_Ok) {
310 FT_Done_Face(face);
311
312 ShowInfo("Unable to use '{}' for {} font, FreeType reported error 0x{:X}, using sprite font instead", font_name, FontSizeToName(fs), error);
313 return nullptr;
314 }
315
316 return std::make_unique<FreeTypeFontCache>(fs, face, size);
317 }
318
319private:
320 static FreeTypeFontCacheFactory instance;
321};
322
323/* static */ FreeTypeFontCacheFactory FreeTypeFontCacheFactory::instance;
324
325#endif /* WITH_FREETYPE */
HasBit
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
Definition bitmath_func.hpp:103
BaseBitSet::Set
constexpr Timpl & Set()
Set all bits.
Definition base_bitset_type.hpp:40
BlitterFactory::GetCurrentBlitter
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
Definition factory.hpp:136
FontCacheFactory
Factory for FontCaches.
Definition fontcache.h:220
FontCache::height
int height
The height of the font.
Definition fontcache.h:27
FontCache::parent
std::unique_ptr< FontCache > parent
The parent of this font cache.
Definition fontcache.h:25
FontCache::fs
const FontSize fs
The size of the font.
Definition fontcache.h:26
FontCache::descender
int descender
The descender value of the font.
Definition fontcache.h:29
FontCache::ascender
int ascender
The ascender value of the font.
Definition fontcache.h:28
FreeTypeFontCacheFactory
Definition freetypefontcache.cpp:212
FreeTypeFontCacheFactory::LoadFont
std::unique_ptr< FontCache > LoadFont(FontSize fs, FontType fonttype) const override
Loads the freetype font.
Definition freetypefontcache.cpp:229
FreeTypeFontCache
Font cache for fonts that are based on a freetype font.
Definition freetypefontcache.cpp:33
FreeTypeFontCache::GetOSHandle
const void * GetOSHandle() override
Get the native OS font handle, if there is one.
Definition freetypefontcache.cpp:47
FreeTypeFontCache::FreeTypeFontCache
FreeTypeFontCache(FontSize fs, FT_Face face, int pixels)
Create a new FreeTypeFontCache.
Definition freetypefontcache.cpp:56
FreeTypeFontCache::~FreeTypeFontCache
~FreeTypeFontCache()
Free everything that was allocated for this font cache.
Definition freetypefontcache.cpp:117
FreeTypeFontCache::ClearFontCache
void ClearFontCache() override
Reset cached glyphs.
Definition freetypefontcache.cpp:127
FreeTypeFontCache::MapCharToGlyph
GlyphID MapCharToGlyph(char32_t key, bool allow_fallback=true) override
Map a character into a glyph.
Definition freetypefontcache.cpp:197
FreeTypeFontCache::face
FT_Face face
The font face associated with this font.
Definition freetypefontcache.cpp:35
FreeTypeFontCache::GetFontName
std::string GetFontName() override
Get the name of this font.
Definition freetypefontcache.cpp:45
FreeTypeFontCache::IsBuiltInFont
bool IsBuiltInFont() override
Is this a built-in sprite font?
Definition freetypefontcache.cpp:46
MissingGlyphSearcher
A searcher for missing glyphs.
Definition strings_func.h:156
SpriteCollMap
Map zoom level to data.
Definition spriteloader.hpp:34
SpriteEncoder::Encode
virtual Sprite * Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator)=0
Convert a sprite from the loader to our own format.
TrueTypeFontCache
Font cache for fonts that are based on a TrueType font.
Definition truetypefontcache.h:22
TrueTypeFontCache::MAX_GLYPH_DIM
static constexpr int MAX_GLYPH_DIM
Maximum glyph dimensions.
Definition truetypefontcache.h:24
TrueTypeFontCache::used_size
int used_size
Used font size.
Definition truetypefontcache.h:28
TrueTypeFontCache::req_size
int req_size
Requested font size.
Definition truetypefontcache.h:27
TrueTypeFontCache::ClearFontCache
void ClearFontCache() override
Reset cached glyphs.
Definition truetypefontcache.cpp:40
TrueTypeFontCache::MAX_FONT_MIN_REC_SIZE
static constexpr uint MAX_FONT_MIN_REC_SIZE
Upper limit for the recommended font size in case a font file contains nonsensical values.
Definition truetypefontcache.h:25
UniquePtrSpriteAllocator
SpriteAllocator that allocates memory via a unique_ptr array.
Definition spritecache.h:20
Debug
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
FioFindFullPath
std::string FioFindFullPath(Subdirectory subdir, std::string_view filename)
Find a path to the filename in one of the search directories.
Definition fileio.cpp:144
BASE_DIR
@ BASE_DIR
Base directory for all subdirectories.
Definition fileio_type.h:89
settings
fluid_settings_t * settings
FluidSynth settings handle.
Definition fluidsynth.cpp:25
GetFontByFaceName
FT_Error GetFontByFaceName(std::string_view font_name, FT_Face *face)
Load a freetype font face with the given font name.
Definition font_unix.cpp:70
GetFontCacheFontSize
uint GetFontCacheFontSize(FontSize fs)
Get the scalable font size to use for a FontSize.
Definition fontcache.cpp:167
GetFontCacheFontName
std::string GetFontCacheFontName(FontSize fs)
Get font to use for a given font size.
Definition fontcache.cpp:211
FontType
FontType
Different types of font that can be loaded.
Definition fontcache.h:214
FontType::TrueType
@ TrueType
Scalable TrueType fonts.
GetFontCacheSubSetting
FontCacheSubSetting * GetFontCacheSubSetting(FontSize fs)
Get the settings of a given font size.
Definition fontcache.h:196
GlyphID
uint32_t GlyphID
Glyphs are characters from a font.
Definition fontcache.h:18
SpriteType::Font
@ Font
A sprite used for fonts.
FontSize
FontSize
Available font sizes.
Definition gfx_type.h:248
FS_SMALL
@ FS_SMALL
Index of the small font in the font tables.
Definition gfx_type.h:250
FS_NORMAL
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:249
abs
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition math_func.hpp:23
SpriteComponent::Palette
@ Palette
Sprite has palette data.
SpriteComponent::Alpha
@ Alpha
Sprite has alpha.
FontCacheSettings
Settings for the four different fonts.
Definition fontcache.h:180
FontCacheSubSetting
Settings for a single font.
Definition fontcache.h:172
SpriteLoader::CommonPixel::m
uint8_t m
Remap-channel.
Definition spriteloader.hpp:53
SpriteLoader::CommonPixel::a
uint8_t a
Alpha-channel.
Definition spriteloader.hpp:52
SpriteLoader::Sprite
Structure for passing information from the sprite loader to the blitter.
Definition spriteloader.hpp:62
SpriteLoader::Sprite::colours
SpriteComponents colours
The colour components of the sprite with useful information.
Definition spriteloader.hpp:67
SpriteLoader::Sprite::AllocateData
void AllocateData(ZoomLevel zoom, size_t size)
Allocate the sprite data of this sprite.
Definition spriteloader.hpp:75
SpriteLoader::Sprite::width
uint16_t width
Width of the sprite.
Definition spriteloader.hpp:64
SpriteLoader::Sprite::x_offs
int16_t x_offs
The x-offset of where the sprite will be drawn.
Definition spriteloader.hpp:65
SpriteLoader::Sprite::data
SpriteLoader::CommonPixel * data
The sprite itself.
Definition spriteloader.hpp:68
SpriteLoader::Sprite::height
uint16_t height
Height of the sprite.
Definition spriteloader.hpp:63
SpriteLoader::Sprite::y_offs
int16_t y_offs
The y-offset of where the sprite will be drawn.
Definition spriteloader.hpp:66
Sprite
Data structure describing a sprite.
Definition spritecache_type.h:16
Sprite::data
std::byte data[]
Sprite data.
Definition spritecache_type.h:21
truetypefontcache.h
Common base definition for font file based font caches.
MAX_FONT_SIZE
static const int MAX_FONT_SIZE
Maximum font size.
Definition truetypefontcache.h:16
ScaleGUITrad
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:49
ZoomLevel::Min
@ Min
Minimum zoom level.