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

OpenTTD Source 14.0-beta3

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 <http://www.gnu.org/licenses/>.
6 */
7
10 #include "../stdafx.h"
11 #include "../debug.h"
12 #include "../fontcache.h"
13 #include "../fontdetection.h"
14 #include "../blitter/factory.hpp"
15 #include "../core/math_func.hpp"
16 #include "../zoom_func.h"
17 #include "../fileio_func.h"
18 #include "../error_func.h"
19 #include "truetypefontcache.h"
20
21 #include "../table/control_codes.h"
22
23 #include "../safeguards.h"
24
25 #ifdef WITH_FREETYPE
26 #include <ft2build.h>
27 #include FT_FREETYPE_H
28 #include FT_GLYPH_H
29 #include FT_TRUETYPE_TABLES_H
30
32 class FreeTypeFontCache : public TrueTypeFontCache {
33 private:
34 FT_Face face;
35
36 void SetFontSize(FontSize fs, FT_Face face, int pixels);
37 const void *InternalGetFontTable(uint32_t tag, size_t &length) override;
38 const Sprite *InternalGetGlyph(GlyphID key, bool aa) override;
39
40 public:
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
50 FT_Library _library = nullptr;
51
52
59 FreeTypeFontCache::FreeTypeFontCache(FontSize fs, FT_Face face, int pixels) : TrueTypeFontCache(fs, pixels), face(face)
60 {
61 assert(face != nullptr);
62
63 this->SetFontSize(fs, face, pixels);
64 }
65
66 void FreeTypeFontCache::SetFontSize(FontSize, FT_Face, int pixels)
67 {
68 if (pixels == 0) {
69 /* Try to determine a good height based on the minimal height recommended by the font. */
70 int scaled_height = ScaleGUITrad(FontCache::GetDefaultFontHeight(this->fs));
71 pixels = scaled_height;
72
73 TT_Header *head = (TT_Header *)FT_Get_Sfnt_Table(this->face, ft_sfnt_head);
74 if (head != nullptr) {
75 /* Font height is minimum height plus the difference between the default
76 * height for this font size and the small size. */
77 int diff = scaled_height - ScaleGUITrad(FontCache::GetDefaultFontHeight(FS_SMALL));
78 /* Clamp() is not used as scaled_height could be greater than MAX_FONT_SIZE, which is not permitted in Clamp(). */
79 pixels = std::min(std::max(std::min<int>(head->Lowest_Rec_PPEM, MAX_FONT_MIN_REC_SIZE) + diff, scaled_height), MAX_FONT_SIZE);
80 }
81 } else {
82 pixels = ScaleGUITrad(pixels);
83 }
84 this->used_size = pixels;
85
86 FT_Error err = FT_Set_Pixel_Sizes(this->face, 0, pixels);
87 if (err != FT_Err_Ok) {
88
89 /* Find nearest size to that requested */
90 FT_Bitmap_Size *bs = this->face->available_sizes;
91 int i = this->face->num_fixed_sizes;
92 if (i > 0) { // In pathetic cases one might get no fixed sizes at all.
93 int n = bs->height;
94 FT_Int chosen = 0;
95 for (; --i; bs++) {
96 if (abs(pixels - bs->height) >= abs(pixels - n)) continue;
97 n = bs->height;
98 chosen = this->face->num_fixed_sizes - i;
99 }
100
101 /* Don't use FT_Set_Pixel_Sizes here - it might give us another
102 * error, even though the size is available (FS#5885). */
103 err = FT_Select_Size(this->face, chosen);
104 }
105 }
106
107 if (err == FT_Err_Ok) {
108 this->units_per_em = this->face->units_per_EM;
109 this->ascender = this->face->size->metrics.ascender >> 6;
110 this->descender = this->face->size->metrics.descender >> 6;
111 this->height = this->ascender - this->descender;
112 } else {
113 /* Both FT_Set_Pixel_Sizes and FT_Select_Size failed. */
114 Debug(fontcache, 0, "Font size selection failed. Using FontCache defaults.");
115 }
116 }
117
118 static FT_Error LoadFont(FontSize fs, FT_Face face, const char *font_name, uint size)
119 {
120 Debug(fontcache, 2, "Requested '{}', using '{} {}'", font_name, face->family_name, face->style_name);
121
122 /* Attempt to select the unicode character map */
123 FT_Error error = FT_Select_Charmap(face, ft_encoding_unicode);
124 if (error == FT_Err_Ok) goto found_face; // Success
125
126 if (error == FT_Err_Invalid_CharMap_Handle) {
127 /* Try to pick a different character map instead. We default to
128 * the first map, but platform_id 0 encoding_id 0 should also
129 * be unicode (strange system...) */
130 FT_CharMap found = face->charmaps[0];
131 int i;
132
133 for (i = 0; i < face->num_charmaps; i++) {
134 FT_CharMap charmap = face->charmaps[i];
135 if (charmap->platform_id == 0 && charmap->encoding_id == 0) {
136 found = charmap;
137 }
138 }
139
140 if (found != nullptr) {
141 error = FT_Set_Charmap(face, found);
142 if (error == FT_Err_Ok) goto found_face;
143 }
144 }
145
146 FT_Done_Face(face);
147 return error;
148
149 found_face:
150 new FreeTypeFontCache(fs, face, size);
151 return FT_Err_Ok;
152 }
153
161 void LoadFreeTypeFont(FontSize fs)
162 {
163 FontCacheSubSetting *settings = GetFontCacheSubSetting(fs);
164
165 if (settings->font.empty()) return;
166
167 if (_library == nullptr) {
168 if (FT_Init_FreeType(&_library) != FT_Err_Ok) {
169 ShowInfo("Unable to initialize FreeType, using sprite fonts instead");
170 return;
171 }
172
173 Debug(fontcache, 2, "Initialized");
174 }
175
176 const char *font_name = settings->font.c_str();
177 FT_Face face = nullptr;
178
179 /* If font is an absolute path to a ttf, try loading that first. */
180 int32_t index = 0;
181 if (settings->os_handle != nullptr) index = *static_cast<const int32_t *>(settings->os_handle);
182 FT_Error error = FT_New_Face(_library, font_name, index, &face);
183
184 if (error != FT_Err_Ok) {
185 /* Check if font is a relative filename in one of our search-paths. */
186 std::string full_font = FioFindFullPath(BASE_DIR, font_name);
187 if (!full_font.empty()) {
188 error = FT_New_Face(_library, full_font.c_str(), 0, &face);
189 }
190 }
191
192 /* Try loading based on font face name (OS-wide fonts). */
193 if (error != FT_Err_Ok) error = GetFontByFaceName(font_name, &face);
194
195 if (error == FT_Err_Ok) {
196 error = LoadFont(fs, face, font_name, settings->size);
197 if (error != FT_Err_Ok) {
198 ShowInfo("Unable to use '{}' for {} font, FreeType reported error 0x{:X}, using sprite font instead", font_name, FontSizeToName(fs), error);
199 }
200 } else {
201 FT_Done_Face(face);
202 }
203 }
204
211 void LoadFreeTypeFont(FontSize fs, const std::string &file_name, uint size)
212 {
213 if (_library == nullptr) {
214 if (FT_Init_FreeType(&_library) != FT_Err_Ok) {
215 ShowInfo("Unable to initialize FreeType, using sprite fonts instead");
216 return;
217 }
218
219 Debug(fontcache, 2, "Initialized");
220 }
221
222 FT_Face face = nullptr;
223 int32_t index = 0;
224 FT_Error error = FT_New_Face(_library, file_name.c_str(), index, &face);
225 if (error == FT_Err_Ok) {
226 LoadFont(fs, face, file_name.c_str(), size);
227 } else {
228 FT_Done_Face(face);
229 }
230 }
231
232
236 FreeTypeFontCache::~FreeTypeFontCache()
237 {
238 FT_Done_Face(this->face);
239 this->face = nullptr;
240 this->ClearFontCache();
241 }
242
246 void FreeTypeFontCache::ClearFontCache()
247 {
248 /* Font scaling might have changed, determine font size anew if it was automatically selected. */
249 if (this->face != nullptr) this->SetFontSize(this->fs, this->face, this->req_size);
250
251 this->TrueTypeFontCache::ClearFontCache();
252 }
253
254
255 const Sprite *FreeTypeFontCache::InternalGetGlyph(GlyphID key, bool aa)
256 {
257 FT_GlyphSlot slot = this->face->glyph;
258
259 FT_Load_Glyph(this->face, key, aa ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO);
260 FT_Render_Glyph(this->face->glyph, aa ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
261
262 /* Despite requesting a normal glyph, FreeType may have returned a bitmap */
263 aa = (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY);
264
265 /* Add 1 scaled pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel */
266 uint shadow = (this->fs == FS_NORMAL) ? ScaleGUITrad(1) : 0;
267 uint width = std::max(1U, (uint)slot->bitmap.width + shadow);
268 uint height = std::max(1U, (uint)slot->bitmap.rows + shadow);
269
270 /* Limit glyph size to prevent overflows later on. */
271 if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) UserError("Font glyph is too large");
272
273 /* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it */
274 SpriteLoader::SpriteCollection spritecollection;
275 SpriteLoader::Sprite &sprite = spritecollection[ZOOM_LVL_NORMAL];
276 sprite.AllocateData(ZOOM_LVL_NORMAL, static_cast<size_t>(width) * height);
277 sprite.type = SpriteType::Font;
278 sprite.colours = (aa ? SCC_PAL | SCC_ALPHA : SCC_PAL);
279 sprite.width = width;
280 sprite.height = height;
281 sprite.x_offs = slot->bitmap_left;
282 sprite.y_offs = this->ascender - slot->bitmap_top;
283
284 /* Draw shadow for medium size */
285 if (this->fs == FS_NORMAL && !aa) {
286 for (uint y = 0; y < (uint)slot->bitmap.rows; y++) {
287 for (uint x = 0; x < (uint)slot->bitmap.width; x++) {
288 if (HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
289 sprite.data[shadow + x + (shadow + y) * sprite.width].m = SHADOW_COLOUR;
290 sprite.data[shadow + x + (shadow + y) * sprite.width].a = 0xFF;
291 }
292 }
293 }
294 }
295
296 for (uint y = 0; y < (uint)slot->bitmap.rows; y++) {
297 for (uint x = 0; x < (uint)slot->bitmap.width; x++) {
298 if (aa ? (slot->bitmap.buffer[x + y * slot->bitmap.pitch] > 0) : HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
299 sprite.data[x + y * sprite.width].m = FACE_COLOUR;
300 sprite.data[x + y * sprite.width].a = aa ? slot->bitmap.buffer[x + y * slot->bitmap.pitch] : 0xFF;
301 }
302 }
303 }
304
305 GlyphEntry new_glyph;
306 new_glyph.sprite = BlitterFactory::GetCurrentBlitter()->Encode(spritecollection, SimpleSpriteAlloc);
307 new_glyph.width = slot->advance.x >> 6;
308
309 this->SetGlyphPtr(key, &new_glyph);
310
311 return new_glyph.sprite;
312 }
313
314
315 GlyphID FreeTypeFontCache::MapCharToGlyph(char32_t key, bool allow_fallback)
316 {
317 assert(IsPrintable(key));
318
319 FT_UInt glyph = FT_Get_Char_Index(this->face, key);
320
321 if (glyph == 0 && allow_fallback && key >= SCC_SPRITE_START && key <= SCC_SPRITE_END) {
322 return this->parent->MapCharToGlyph(key);
323 }
324
325 return glyph;
326 }
327
328 const void *FreeTypeFontCache::InternalGetFontTable(uint32_t tag, size_t &length)
329 {
330 FT_ULong len = 0;
331 FT_Byte *result = nullptr;
332
333 FT_Load_Sfnt_Table(this->face, tag, 0, nullptr, &len);
334
335 if (len > 0) {
336 result = MallocT<FT_Byte>(len);
337 FT_Load_Sfnt_Table(this->face, tag, 0, result, &len);
338 }
339
340 length = len;
341 return result;
342 }
343
347 void UninitFreeType()
348 {
349 FT_Done_FreeType(_library);
350 _library = nullptr;
351 }
352
353 #if !defined(WITH_FONTCONFIG)
354
355 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) { return FT_Err_Cannot_Open_Resource; }
356
357 #endif /* !defined(WITH_FONTCONFIG) */
358
359 #endif /* WITH_FREETYPE */
SCC_PAL
@ SCC_PAL
Sprite has palette data.
Definition: spriteloader.hpp:25
SpriteLoader::Sprite::x_offs
int16_t x_offs
The x-offset of where the sprite will be drawn.
Definition: spriteloader.hpp:51
TrueTypeFontCache
Font cache for fonts that are based on a TrueType font.
Definition: truetypefontcache.h:22
FreeTypeFontCache::GetFontName
std::string GetFontName() override
Get the name of this font.
Definition: freetypefontcache.cpp:45
FontCacheSubSetting
Settings for a single font.
Definition: fontcache.h:207
SpriteLoader::Sprite::AllocateData
void AllocateData(ZoomLevel zoom, size_t size)
Allocate the sprite data of this sprite.
Definition: spriteloader.hpp:62
FontCache::height
int height
The height of the font.
Definition: fontcache.h:26
FioFindFullPath
std::string FioFindFullPath(Subdirectory subdir, const std::string &filename)
Find a path to the filename in one of the search directories.
Definition: fileio.cpp:159
FreeTypeFontCache::GetOSHandle
const void * GetOSHandle() override
Get the native OS font handle, if there is one.
Definition: freetypefontcache.cpp:47
FreeTypeFontCache
Font cache for fonts that are based on a freetype font.
Definition: freetypefontcache.cpp:32
FreeTypeFontCache::~FreeTypeFontCache
~FreeTypeFontCache()
Free everything that was allocated for this font cache.
Definition: freetypefontcache.cpp:236
FreeTypeFontCache::IsBuiltInFont
bool IsBuiltInFont() override
Is this a built-in sprite font?
Definition: freetypefontcache.cpp:46
SpriteLoader::Sprite::height
uint16_t height
Height of the sprite.
Definition: spriteloader.hpp:49
SpriteType::Font
@ Font
A sprite used for fonts.
SpriteLoader::SpriteCollection
std::array< Sprite, ZOOM_LVL_END > SpriteCollection
Type defining a collection of sprites, one for each zoom level.
Definition: spriteloader.hpp:71
LoadFreeTypeFont
void LoadFreeTypeFont(FontSize fs)
Loads the freetype font.
Definition: freetypefontcache.cpp:161
Debug
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
SpriteLoader::Sprite::data
SpriteLoader::CommonPixel * data
The sprite itself.
Definition: spriteloader.hpp:55
UninitFreeType
void UninitFreeType()
Free everything allocated w.r.t.
Definition: freetypefontcache.cpp:347
ScaleGUITrad
int ScaleGUITrad(int value)
Scale traditional pixel dimensions to GUI zoom level.
Definition: zoom_func.h:117
FontCache::units_per_em
int units_per_em
The units per EM value of the font.
Definition: fontcache.h:29
BASE_DIR
@ BASE_DIR
Base directory for all subdirectories.
Definition: fileio_type.h:109
FreeTypeFontCache::face
FT_Face face
The font face associated with this font.
Definition: freetypefontcache.cpp:34
TrueTypeFontCache::MAX_GLYPH_DIM
static constexpr int MAX_GLYPH_DIM
Maximum glyph dimensions.
Definition: truetypefontcache.h:24
FS_NORMAL
@ FS_NORMAL
Index of the normal font in the font tables.
Definition: gfx_type.h:203
SpriteLoader::Sprite::type
SpriteType type
The sprite type.
Definition: spriteloader.hpp:53
FS_SMALL
@ FS_SMALL
Index of the small font in the font tables.
Definition: gfx_type.h:204
Sprite::width
uint16_t width
Width of the sprite.
Definition: spritecache.h:19
SpriteLoader::CommonPixel::m
uint8_t m
Remap-channel.
Definition: spriteloader.hpp:39
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
BlitterFactory::GetCurrentBlitter
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
Definition: factory.hpp:138
truetypefontcache.h
settings
fluid_settings_t * settings
FluidSynth settings handle.
Definition: fluidsynth.cpp:21
GetFontCacheSubSetting
FontCacheSubSetting * GetFontCacheSubSetting(FontSize fs)
Get the settings of a given font size.
Definition: fontcache.h:232
GlyphID
uint32_t GlyphID
Glyphs are characters from a font.
Definition: fontcache.h:17
GetFontByFaceName
FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
Load a freetype font face with the given font name.
Definition: font_unix.cpp:42
FontCache::fs
const FontSize fs
The size of the font.
Definition: fontcache.h:25
SpriteLoader::Sprite::colours
SpriteColourComponent colours
The colour components of the sprite with useful information.
Definition: spriteloader.hpp:54
SpriteLoader::Sprite::y_offs
int16_t y_offs
The y-offset of where the sprite will be drawn.
Definition: spriteloader.hpp:52
TrueTypeFontCache::req_size
int req_size
Requested font size.
Definition: truetypefontcache.h:27
FontCache::descender
int descender
The descender value of the font.
Definition: fontcache.h:28
FontCache::MapCharToGlyph
virtual GlyphID MapCharToGlyph(char32_t key, bool fallback=true)=0
Map a character into a glyph.
SpriteLoader::Sprite::width
uint16_t width
Width of the sprite.
Definition: spriteloader.hpp:50
FreeTypeFontCache::FreeTypeFontCache
FreeTypeFontCache(FontSize fs, FT_Face face, int pixels)
Create a new FreeTypeFontCache.
Definition: freetypefontcache.cpp:59
abs
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition: math_func.hpp:23
FontCache::parent
FontCache * parent
The parent of this font cache.
Definition: fontcache.h:24
SCC_ALPHA
@ SCC_ALPHA
Sprite has alpha.
Definition: spriteloader.hpp:24
FontCache::ascender
int ascender
The ascender value of the font.
Definition: fontcache.h:27
TrueTypeFontCache::ClearFontCache
void ClearFontCache() override
Reset cached glyphs.
Definition: truetypefontcache.cpp:45
SpriteLoader::Sprite
Structure for passing information from the sprite loader to the blitter.
Definition: spriteloader.hpp:48
TrueTypeFontCache::used_size
int used_size
Used font size.
Definition: truetypefontcache.h:28
ZOOM_LVL_NORMAL
@ ZOOM_LVL_NORMAL
The normal zoom level.
Definition: zoom_type.h:22
FontSize
FontSize
Available font sizes.
Definition: gfx_type.h:202
SimpleSpriteAlloc
void * SimpleSpriteAlloc(size_t size)
Sprite allocator simply using malloc.
Definition: spritecache.cpp:880
Sprite
Data structure describing a sprite.
Definition: spritecache.h:17
FreeTypeFontCache::ClearFontCache
void ClearFontCache() override
Reset cached glyphs.
Definition: freetypefontcache.cpp:246
SpriteLoader::CommonPixel::a
uint8_t a
Alpha-channel.
Definition: spriteloader.hpp:38
MAX_FONT_SIZE
static const int MAX_FONT_SIZE
Maximum font size.
Definition: truetypefontcache.h:16
FreeTypeFontCache::MapCharToGlyph
GlyphID MapCharToGlyph(char32_t key, bool allow_fallback=true) override
Map a character into a glyph.
Definition: freetypefontcache.cpp:315
SpriteEncoder::Encode
virtual Sprite * Encode(const SpriteLoader::SpriteCollection &sprite, AllocatorProc *allocator)=0
Convert a sprite from the loader to our own format.
HasBit
constexpr debug_inline bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
Definition: bitmath_func.hpp:103