OpenTTD Source  13.0-beta2
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 "truetypefontcache.h"
19 
20 #include "../table/control_codes.h"
21 
22 #include "../safeguards.h"
23 
24 #ifdef WITH_FREETYPE
25 #include <ft2build.h>
26 #include FT_FREETYPE_H
27 #include FT_GLYPH_H
28 #include FT_TRUETYPE_TABLES_H
29 
32 private:
33  FT_Face face;
34 
35  void SetFontSize(FontSize fs, FT_Face face, int pixels);
36  virtual const void *InternalGetFontTable(uint32 tag, size_t &length);
37  virtual const Sprite *InternalGetGlyph(GlyphID key, bool aa);
38 
39 public:
40  FreeTypeFontCache(FontSize fs, FT_Face face, int pixels);
42  virtual void ClearFontCache();
43  virtual GlyphID MapCharToGlyph(WChar key);
44  virtual const char *GetFontName() { return face->family_name; }
45  virtual bool IsBuiltInFont() { return false; }
46 };
47 
48 FT_Library _library = nullptr;
49 
50 
57 FreeTypeFontCache::FreeTypeFontCache(FontSize fs, FT_Face face, int pixels) : TrueTypeFontCache(fs, pixels), face(face)
58 {
59  assert(face != nullptr);
60 
61  this->SetFontSize(fs, face, pixels);
62 }
63 
64 void FreeTypeFontCache::SetFontSize(FontSize fs, FT_Face face, int pixels)
65 {
66  if (pixels == 0) {
67  /* Try to determine a good height based on the minimal height recommended by the font. */
68  int scaled_height = ScaleGUITrad(this->GetDefaultFontHeight(this->fs));
69  pixels = scaled_height;
70 
71  TT_Header *head = (TT_Header *)FT_Get_Sfnt_Table(this->face, ft_sfnt_head);
72  if (head != nullptr) {
73  /* Font height is minimum height plus the difference between the default
74  * height for this font size and the small size. */
75  int diff = scaled_height - ScaleGUITrad(this->GetDefaultFontHeight(FS_SMALL));
76  /* Clamp() is not used as scaled_height could be greater than MAX_FONT_SIZE, which is not permitted in Clamp(). */
77  pixels = std::min(std::max(std::min<int>(head->Lowest_Rec_PPEM, MAX_FONT_MIN_REC_SIZE) + diff, scaled_height), MAX_FONT_SIZE);
78  }
79  } else {
80  pixels = ScaleGUITrad(pixels);
81  }
82  this->used_size = pixels;
83 
84  FT_Error err = FT_Set_Pixel_Sizes(this->face, 0, pixels);
85  if (err != FT_Err_Ok) {
86 
87  /* Find nearest size to that requested */
88  FT_Bitmap_Size *bs = this->face->available_sizes;
89  int i = this->face->num_fixed_sizes;
90  if (i > 0) { // In pathetic cases one might get no fixed sizes at all.
91  int n = bs->height;
92  FT_Int chosen = 0;
93  for (; --i; bs++) {
94  if (abs(pixels - bs->height) >= abs(pixels - n)) continue;
95  n = bs->height;
96  chosen = this->face->num_fixed_sizes - i;
97  }
98 
99  /* Don't use FT_Set_Pixel_Sizes here - it might give us another
100  * error, even though the size is available (FS#5885). */
101  err = FT_Select_Size(this->face, chosen);
102  }
103  }
104 
105  if (err == FT_Err_Ok) {
106  this->units_per_em = this->face->units_per_EM;
107  this->ascender = this->face->size->metrics.ascender >> 6;
108  this->descender = this->face->size->metrics.descender >> 6;
109  this->height = this->ascender - this->descender;
110  } else {
111  /* Both FT_Set_Pixel_Sizes and FT_Select_Size failed. */
112  Debug(fontcache, 0, "Font size selection failed. Using FontCache defaults.");
113  }
114 }
115 
124 {
125  FontCacheSubSetting *settings = nullptr;
126  switch (fs) {
127  default: NOT_REACHED();
128  case FS_SMALL: settings = &_fcsettings.small; break;
129  case FS_NORMAL: settings = &_fcsettings.medium; break;
130  case FS_LARGE: settings = &_fcsettings.large; break;
131  case FS_MONO: settings = &_fcsettings.mono; break;
132  }
133 
134  if (settings->font.empty()) return;
135 
136  if (_library == nullptr) {
137  if (FT_Init_FreeType(&_library) != FT_Err_Ok) {
138  ShowInfoF("Unable to initialize FreeType, using sprite fonts instead");
139  return;
140  }
141 
142  Debug(fontcache, 2, "Initialized");
143  }
144 
145  const char *font_name = settings->font.c_str();
146  FT_Face face = nullptr;
147 
148  /* If font is an absolute path to a ttf, try loading that first. */
149  FT_Error error = FT_New_Face(_library, font_name, 0, &face);
150 
151 #if defined(WITH_COCOA)
152  extern void MacOSRegisterExternalFont(const char *file_path);
153  if (error == FT_Err_Ok) MacOSRegisterExternalFont(font_name);
154 #endif
155 
156  if (error != FT_Err_Ok) {
157  /* Check if font is a relative filename in one of our search-paths. */
158  std::string full_font = FioFindFullPath(BASE_DIR, font_name);
159  if (!full_font.empty()) {
160  error = FT_New_Face(_library, full_font.c_str(), 0, &face);
161 #if defined(WITH_COCOA)
162  if (error == FT_Err_Ok) MacOSRegisterExternalFont(full_font.c_str());
163 #endif
164  }
165  }
166 
167  /* Try loading based on font face name (OS-wide fonts). */
168  if (error != FT_Err_Ok) error = GetFontByFaceName(font_name, &face);
169 
170  if (error == FT_Err_Ok) {
171  Debug(fontcache, 2, "Requested '{}', using '{} {}'", font_name, face->family_name, face->style_name);
172 
173  /* Attempt to select the unicode character map */
174  error = FT_Select_Charmap(face, ft_encoding_unicode);
175  if (error == FT_Err_Ok) goto found_face; // Success
176 
177  if (error == FT_Err_Invalid_CharMap_Handle) {
178  /* Try to pick a different character map instead. We default to
179  * the first map, but platform_id 0 encoding_id 0 should also
180  * be unicode (strange system...) */
181  FT_CharMap found = face->charmaps[0];
182  int i;
183 
184  for (i = 0; i < face->num_charmaps; i++) {
185  FT_CharMap charmap = face->charmaps[i];
186  if (charmap->platform_id == 0 && charmap->encoding_id == 0) {
187  found = charmap;
188  }
189  }
190 
191  if (found != nullptr) {
192  error = FT_Set_Charmap(face, found);
193  if (error == FT_Err_Ok) goto found_face;
194  }
195  }
196  }
197 
198  FT_Done_Face(face);
199 
200  static const char *SIZE_TO_NAME[] = { "medium", "small", "large", "mono" };
201  ShowInfoF("Unable to use '%s' for %s font, FreeType reported error 0x%X, using sprite font instead", font_name, SIZE_TO_NAME[fs], error);
202  return;
203 
204 found_face:
205  new FreeTypeFontCache(fs, face, settings->size);
206 }
207 
208 
213 {
214  FT_Done_Face(this->face);
215  this->face = nullptr;
216  this->ClearFontCache();
217 }
218 
223 {
224  /* Font scaling might have changed, determine font size anew if it was automatically selected. */
225  if (this->face != nullptr) this->SetFontSize(this->fs, this->face, this->req_size);
226 
228 }
229 
230 
231 const Sprite *FreeTypeFontCache::InternalGetGlyph(GlyphID key, bool aa)
232 {
233  FT_GlyphSlot slot = this->face->glyph;
234 
235  FT_Load_Glyph(this->face, key, aa ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO);
236  FT_Render_Glyph(this->face->glyph, aa ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
237 
238  /* Despite requesting a normal glyph, FreeType may have returned a bitmap */
239  aa = (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY);
240 
241  /* Add 1 scaled pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel */
242  uint shadow = (this->fs == FS_NORMAL) ? ScaleGUITrad(1) : 0;
243  uint width = std::max(1U, (uint)slot->bitmap.width + shadow);
244  uint height = std::max(1U, (uint)slot->bitmap.rows + shadow);
245 
246  /* Limit glyph size to prevent overflows later on. */
247  if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) usererror("Font glyph is too large");
248 
249  /* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it */
250  SpriteLoader::Sprite sprite;
251  sprite.AllocateData(ZOOM_LVL_NORMAL, width * height);
252  sprite.type = ST_FONT;
253  sprite.colours = (aa ? SCC_PAL | SCC_ALPHA : SCC_PAL);
254  sprite.width = width;
255  sprite.height = height;
256  sprite.x_offs = slot->bitmap_left;
257  sprite.y_offs = this->ascender - slot->bitmap_top;
258 
259  /* Draw shadow for medium size */
260  if (this->fs == FS_NORMAL && !aa) {
261  for (uint y = 0; y < (uint)slot->bitmap.rows; y++) {
262  for (uint x = 0; x < (uint)slot->bitmap.width; x++) {
263  if (HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
264  sprite.data[shadow + x + (shadow + y) * sprite.width].m = SHADOW_COLOUR;
265  sprite.data[shadow + x + (shadow + y) * sprite.width].a = 0xFF;
266  }
267  }
268  }
269  }
270 
271  for (uint y = 0; y < (uint)slot->bitmap.rows; y++) {
272  for (uint x = 0; x < (uint)slot->bitmap.width; x++) {
273  if (aa ? (slot->bitmap.buffer[x + y * slot->bitmap.pitch] > 0) : HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
274  sprite.data[x + y * sprite.width].m = FACE_COLOUR;
275  sprite.data[x + y * sprite.width].a = aa ? slot->bitmap.buffer[x + y * slot->bitmap.pitch] : 0xFF;
276  }
277  }
278  }
279 
280  GlyphEntry new_glyph;
281  new_glyph.sprite = BlitterFactory::GetCurrentBlitter()->Encode(&sprite, SimpleSpriteAlloc);
282  new_glyph.width = slot->advance.x >> 6;
283 
284  this->SetGlyphPtr(key, &new_glyph);
285 
286  return new_glyph.sprite;
287 }
288 
289 
291 {
292  assert(IsPrintable(key));
293 
294  if (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END) {
295  return this->parent->MapCharToGlyph(key);
296  }
297 
298  return FT_Get_Char_Index(this->face, key);
299 }
300 
301 const void *FreeTypeFontCache::InternalGetFontTable(uint32 tag, size_t &length)
302 {
303  FT_ULong len = 0;
304  FT_Byte *result = nullptr;
305 
306  FT_Load_Sfnt_Table(this->face, tag, 0, nullptr, &len);
307 
308  if (len > 0) {
309  result = MallocT<FT_Byte>(len);
310  FT_Load_Sfnt_Table(this->face, tag, 0, result, &len);
311  }
312 
313  length = len;
314  return result;
315 }
316 
321 {
322  FT_Done_FreeType(_library);
323  _library = nullptr;
324 }
325 
326 #if !defined(_WIN32) && !defined(__APPLE__) && !defined(WITH_FONTCONFIG) && !defined(WITH_COCOA)
327 
328 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) { return FT_Err_Cannot_Open_Resource; }
329 
330 #endif /* !defined(_WIN32) && !defined(__APPLE__) && !defined(WITH_FONTCONFIG) && !defined(WITH_COCOA) */
331 
332 #endif /* WITH_FREETYPE */
SpriteLoader::CommonPixel::m
uint8 m
Remap-channel.
Definition: spriteloader.hpp:39
SCC_PAL
@ SCC_PAL
Sprite has palette data.
Definition: spriteloader.hpp:25
GlyphID
uint32 GlyphID
Glyphs are characters from a font.
Definition: fontcache.h:17
TrueTypeFontCache
Font cache for fonts that are based on a TrueType font.
Definition: truetypefontcache.h:23
WChar
char32_t WChar
Type for wide characters, i.e.
Definition: string_type.h:36
usererror
void CDECL usererror(const char *s,...)
Error handling for fatal user errors.
Definition: openttd.cpp:105
ST_FONT
@ ST_FONT
A sprite used for fonts.
Definition: gfx_type.h:304
FontCacheSubSetting
Settings for a single font.
Definition: fontcache.h:203
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:27
FreeTypeFontCache
Font cache for fonts that are based on a freetype font.
Definition: freetypefontcache.cpp:31
FreeTypeFontCache::~FreeTypeFontCache
~FreeTypeFontCache()
Free everything that was allocated for this font cache.
Definition: freetypefontcache.cpp:212
HasBit
static bool HasBit(const T x, const uint8 y)
Checks if a bit in a value is set.
Definition: bitmath_func.hpp:103
FS_LARGE
@ FS_LARGE
Index of the large font in the font tables.
Definition: gfx_type.h:209
FreeTypeFontCache::ClearFontCache
virtual void ClearFontCache()
Reset cached glyphs.
Definition: freetypefontcache.cpp:222
SpriteEncoder::Encode
virtual Sprite * Encode(const SpriteLoader::Sprite *sprite, AllocatorProc *allocator)=0
Convert a sprite from the loader to our own format.
LoadFreeTypeFont
void LoadFreeTypeFont(FontSize fs)
Loads the freetype font.
Definition: freetypefontcache.cpp:123
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:320
FontCache::units_per_em
int units_per_em
The units per EM value of the font.
Definition: fontcache.h:30
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:33
TrueTypeFontCache::MAX_GLYPH_DIM
static constexpr int MAX_GLYPH_DIM
Maximum glyph dimensions.
Definition: truetypefontcache.h:25
FS_NORMAL
@ FS_NORMAL
Index of the normal font in the font tables.
Definition: gfx_type.h:207
SpriteLoader::Sprite::type
SpriteType type
The sprite type.
Definition: spriteloader.hpp:53
FreeTypeFontCache::GetFontName
virtual const char * GetFontName()
Get the name of this font.
Definition: freetypefontcache.cpp:44
FreeTypeFontCache::IsBuiltInFont
virtual bool IsBuiltInFont()
Is this a built-in sprite font?
Definition: freetypefontcache.cpp:45
FS_SMALL
@ FS_SMALL
Index of the small font in the font tables.
Definition: gfx_type.h:208
FontCacheSettings::large
FontCacheSubSetting large
The largest font; mostly used for newspapers.
Definition: fontcache.h:215
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:26
BlitterFactory::GetCurrentBlitter
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
Definition: factory.hpp:141
truetypefontcache.h
Sprite::width
uint16 width
Width of the sprite.
Definition: spritecache.h:19
FontCacheSettings::medium
FontCacheSubSetting medium
The normal font size.
Definition: fontcache.h:214
SpriteLoader::Sprite::x_offs
int16 x_offs
The x-offset of where the sprite will be drawn.
Definition: spriteloader.hpp:51
settings
fluid_settings_t * settings
FluidSynth settings handle.
Definition: fluidsynth.cpp:21
GetFontByFaceName
FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
Load a freetype font face with the given font name.
Definition: font_osx.cpp:35
FontCache::fs
const FontSize fs
The size of the font.
Definition: fontcache.h:26
SpriteLoader::Sprite::colours
SpriteColourComponent colours
The colour components of the sprite with useful information.
Definition: spriteloader.hpp:54
MacOSRegisterExternalFont
void MacOSRegisterExternalFont(const char *file_path)
Register an external font file with the CoreText system.
Definition: string_osx.cpp:297
TrueTypeFontCache::req_size
int req_size
Requested font size.
Definition: truetypefontcache.h:28
FontCache::descender
int descender
The descender value of the font.
Definition: fontcache.h:29
SpriteLoader::Sprite::width
uint16 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:57
FontCache::parent
FontCache * parent
The parent of this font cache.
Definition: fontcache.h:25
SCC_ALPHA
@ SCC_ALPHA
Sprite has alpha.
Definition: spriteloader.hpp:24
FontCacheSettings::mono
FontCacheSubSetting mono
The mono space font used for license/readme viewers.
Definition: fontcache.h:216
ShowInfoF
void CDECL ShowInfoF(const char *str,...)
Shows some information on the console/a popup box depending on the OS.
Definition: openttd.cpp:156
FontCache::ascender
int ascender
The ascender value of the font.
Definition: fontcache.h:28
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
abs
static T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition: math_func.hpp:21
TrueTypeFontCache::used_size
int used_size
Used font size.
Definition: truetypefontcache.h:29
error
void CDECL error(const char *s,...)
Error handling for fatal non-user errors.
Definition: openttd.cpp:134
Debug
#define Debug(name, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
FS_MONO
@ FS_MONO
Index of the monospaced font in the font tables.
Definition: gfx_type.h:210
SpriteLoader::CommonPixel::a
uint8 a
Alpha-channel.
Definition: spriteloader.hpp:38
FreeTypeFontCache::MapCharToGlyph
virtual GlyphID MapCharToGlyph(WChar key)
Map a character into a glyph.
Definition: freetypefontcache.cpp:290
ZOOM_LVL_NORMAL
@ ZOOM_LVL_NORMAL
The normal zoom level.
Definition: zoom_type.h:22
FontSize
FontSize
Available font sizes.
Definition: gfx_type.h:206
SpriteLoader::Sprite::y_offs
int16 y_offs
The y-offset of where the sprite will be drawn.
Definition: spriteloader.hpp:52
SpriteLoader::Sprite::height
uint16 height
Height of the sprite.
Definition: spriteloader.hpp:49
FioFindFullPath
std::string FioFindFullPath(Subdirectory subdir, const char *filename)
Find a path to the filename in one of the search directories.
Definition: fileio.cpp:141
SimpleSpriteAlloc
void * SimpleSpriteAlloc(size_t size)
Sprite allocator simply using malloc.
Definition: spritecache.cpp:882
FontCache::MapCharToGlyph
virtual GlyphID MapCharToGlyph(WChar key)=0
Map a character into a glyph.
FontCacheSettings::small
FontCacheSubSetting small
The smallest font; mostly used for zoomed out view.
Definition: fontcache.h:213
Sprite
Data structure describing a sprite.
Definition: spritecache.h:17
MAX_FONT_SIZE
static const int MAX_FONT_SIZE
Maximum font size.
Definition: truetypefontcache.h:17
ScaleGUITrad
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition: widget.cpp:168