Fix #12973: Don't exclude high score after using sandbox
[openttd-github.git] / src / fontcache / freetypefontcache.cpp
blob9c9ccaaeebcb6bf00c47acbb0202a2cf6152bfb5
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 */
8 /** @file freetypefontcache.cpp FreeType font cache implementation. */
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"
21 #include "../table/control_codes.h"
23 #include "../safeguards.h"
25 #ifdef WITH_FREETYPE
26 #include <ft2build.h>
27 #include FT_FREETYPE_H
28 #include FT_GLYPH_H
29 #include FT_TRUETYPE_TABLES_H
31 /** Font cache for fonts that are based on a freetype font. */
32 class FreeTypeFontCache : public TrueTypeFontCache {
33 private:
34 FT_Face face; ///< The font face associated with this font.
36 void SetFontSize(int pixels);
37 const Sprite *InternalGetGlyph(GlyphID key, bool aa) override;
39 public:
40 FreeTypeFontCache(FontSize fs, FT_Face face, int pixels);
41 ~FreeTypeFontCache();
42 void ClearFontCache() override;
43 GlyphID MapCharToGlyph(char32_t key, bool allow_fallback = true) override;
44 std::string GetFontName() override { return fmt::format("{}, {}", face->family_name, face->style_name); }
45 bool IsBuiltInFont() override { return false; }
46 const void *GetOSHandle() override { return &face; }
49 FT_Library _library = nullptr;
52 /**
53 * Create a new FreeTypeFontCache.
54 * @param fs The font size that is going to be cached.
55 * @param face The font that has to be loaded.
56 * @param pixels The number of pixels this font should be high.
58 FreeTypeFontCache::FreeTypeFontCache(FontSize fs, FT_Face face, int pixels) : TrueTypeFontCache(fs, pixels), face(face)
60 assert(face != nullptr);
62 this->SetFontSize(pixels);
65 void FreeTypeFontCache::SetFontSize(int pixels)
67 if (pixels == 0) {
68 /* Try to determine a good height based on the minimal height recommended by the font. */
69 int scaled_height = ScaleGUITrad(FontCache::GetDefaultFontHeight(this->fs));
70 pixels = scaled_height;
72 TT_Header *head = (TT_Header *)FT_Get_Sfnt_Table(this->face, ft_sfnt_head);
73 if (head != nullptr) {
74 /* Font height is minimum height plus the difference between the default
75 * height for this font size and the small size. */
76 int diff = scaled_height - ScaleGUITrad(FontCache::GetDefaultFontHeight(FS_SMALL));
77 /* Clamp() is not used as scaled_height could be greater than MAX_FONT_SIZE, which is not permitted in Clamp(). */
78 pixels = std::min(std::max(std::min<int>(head->Lowest_Rec_PPEM, MAX_FONT_MIN_REC_SIZE) + diff, scaled_height), MAX_FONT_SIZE);
80 } else {
81 pixels = ScaleGUITrad(pixels);
83 this->used_size = pixels;
85 FT_Error err = FT_Set_Pixel_Sizes(this->face, 0, pixels);
86 if (err != FT_Err_Ok) {
88 /* Find nearest size to that requested */
89 FT_Bitmap_Size *bs = this->face->available_sizes;
90 int i = this->face->num_fixed_sizes;
91 if (i > 0) { // In pathetic cases one might get no fixed sizes at all.
92 int n = bs->height;
93 FT_Int chosen = 0;
94 for (; --i; bs++) {
95 if (abs(pixels - bs->height) >= abs(pixels - n)) continue;
96 n = bs->height;
97 chosen = this->face->num_fixed_sizes - i;
100 /* Don't use FT_Set_Pixel_Sizes here - it might give us another
101 * error, even though the size is available (FS#5885). */
102 err = FT_Select_Size(this->face, chosen);
106 if (err == FT_Err_Ok) {
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.");
116 static FT_Error LoadFont(FontSize fs, FT_Face face, const char *font_name, uint size)
118 Debug(fontcache, 2, "Requested '{}', using '{} {}'", font_name, face->family_name, face->style_name);
120 /* Attempt to select the unicode character map */
121 FT_Error error = FT_Select_Charmap(face, ft_encoding_unicode);
122 if (error == FT_Err_Ok) goto found_face; // Success
124 if (error == FT_Err_Invalid_CharMap_Handle) {
125 /* Try to pick a different character map instead. We default to
126 * the first map, but platform_id 0 encoding_id 0 should also
127 * be unicode (strange system...) */
128 FT_CharMap found = face->charmaps[0];
129 int i;
131 for (i = 0; i < face->num_charmaps; i++) {
132 FT_CharMap charmap = face->charmaps[i];
133 if (charmap->platform_id == 0 && charmap->encoding_id == 0) {
134 found = charmap;
138 if (found != nullptr) {
139 error = FT_Set_Charmap(face, found);
140 if (error == FT_Err_Ok) goto found_face;
144 FT_Done_Face(face);
145 return error;
147 found_face:
148 new FreeTypeFontCache(fs, face, size);
149 return FT_Err_Ok;
153 * Loads the freetype font.
154 * First type to load the fontname as if it were a path. If that fails,
155 * try to resolve the filename of the font using fontconfig, where the
156 * format is 'font family name' or 'font family name, font style'.
157 * @param fs The font size to load.
159 void LoadFreeTypeFont(FontSize fs)
161 FontCacheSubSetting *settings = GetFontCacheSubSetting(fs);
163 std::string font = GetFontCacheFontName(fs);
164 if (font.empty()) return;
166 if (_library == nullptr) {
167 if (FT_Init_FreeType(&_library) != FT_Err_Ok) {
168 ShowInfo("Unable to initialize FreeType, using sprite fonts instead");
169 return;
172 Debug(fontcache, 2, "Initialized");
175 const char *font_name = font.c_str();
176 FT_Face face = nullptr;
178 /* If font is an absolute path to a ttf, try loading that first. */
179 int32_t index = 0;
180 if (settings->os_handle != nullptr) index = *static_cast<const int32_t *>(settings->os_handle);
181 FT_Error error = FT_New_Face(_library, font_name, index, &face);
183 if (error != FT_Err_Ok) {
184 /* Check if font is a relative filename in one of our search-paths. */
185 std::string full_font = FioFindFullPath(BASE_DIR, font_name);
186 if (!full_font.empty()) {
187 error = FT_New_Face(_library, full_font.c_str(), 0, &face);
191 /* Try loading based on font face name (OS-wide fonts). */
192 if (error != FT_Err_Ok) error = GetFontByFaceName(font_name, &face);
194 if (error == FT_Err_Ok) {
195 error = LoadFont(fs, face, font_name, GetFontCacheFontSize(fs));
196 if (error != FT_Err_Ok) {
197 ShowInfo("Unable to use '{}' for {} font, FreeType reported error 0x{:X}, using sprite font instead", font_name, FontSizeToName(fs), error);
199 } else {
200 FT_Done_Face(face);
205 * Free everything that was allocated for this font cache.
207 FreeTypeFontCache::~FreeTypeFontCache()
209 FT_Done_Face(this->face);
210 this->face = nullptr;
211 this->ClearFontCache();
215 * Reset cached glyphs.
217 void FreeTypeFontCache::ClearFontCache()
219 /* Font scaling might have changed, determine font size anew if it was automatically selected. */
220 if (this->face != nullptr) this->SetFontSize(this->req_size);
222 this->TrueTypeFontCache::ClearFontCache();
226 const Sprite *FreeTypeFontCache::InternalGetGlyph(GlyphID key, bool aa)
228 FT_GlyphSlot slot = this->face->glyph;
230 FT_Load_Glyph(this->face, key, aa ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO);
231 FT_Render_Glyph(this->face->glyph, aa ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
233 /* Despite requesting a normal glyph, FreeType may have returned a bitmap */
234 aa = (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY);
236 /* Add 1 scaled pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel */
237 uint shadow = (this->fs == FS_NORMAL) ? ScaleGUITrad(1) : 0;
238 uint width = std::max(1U, (uint)slot->bitmap.width + shadow);
239 uint height = std::max(1U, (uint)slot->bitmap.rows + shadow);
241 /* Limit glyph size to prevent overflows later on. */
242 if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) UserError("Font glyph is too large");
244 /* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it */
245 SpriteLoader::SpriteCollection spritecollection;
246 SpriteLoader::Sprite &sprite = spritecollection[ZOOM_LVL_MIN];
247 sprite.AllocateData(ZOOM_LVL_MIN, static_cast<size_t>(width) * height);
248 sprite.type = SpriteType::Font;
249 sprite.colours = (aa ? SCC_PAL | SCC_ALPHA : SCC_PAL);
250 sprite.width = width;
251 sprite.height = height;
252 sprite.x_offs = slot->bitmap_left;
253 sprite.y_offs = this->ascender - slot->bitmap_top;
255 /* Draw shadow for medium size */
256 if (this->fs == FS_NORMAL && !aa) {
257 for (uint y = 0; y < (uint)slot->bitmap.rows; y++) {
258 for (uint x = 0; x < (uint)slot->bitmap.width; x++) {
259 if (HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
260 sprite.data[shadow + x + (shadow + y) * sprite.width].m = SHADOW_COLOUR;
261 sprite.data[shadow + x + (shadow + y) * sprite.width].a = 0xFF;
267 for (uint y = 0; y < (uint)slot->bitmap.rows; y++) {
268 for (uint x = 0; x < (uint)slot->bitmap.width; x++) {
269 if (aa ? (slot->bitmap.buffer[x + y * slot->bitmap.pitch] > 0) : HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
270 sprite.data[x + y * sprite.width].m = FACE_COLOUR;
271 sprite.data[x + y * sprite.width].a = aa ? slot->bitmap.buffer[x + y * slot->bitmap.pitch] : 0xFF;
276 UniquePtrSpriteAllocator allocator;
277 BlitterFactory::GetCurrentBlitter()->Encode(spritecollection, allocator);
279 GlyphEntry new_glyph;
280 new_glyph.data = std::move(allocator.data);
281 new_glyph.width = slot->advance.x >> 6;
283 return this->SetGlyphPtr(key, std::move(new_glyph)).GetSprite();
287 GlyphID FreeTypeFontCache::MapCharToGlyph(char32_t key, bool allow_fallback)
289 assert(IsPrintable(key));
291 FT_UInt glyph = FT_Get_Char_Index(this->face, key);
293 if (glyph == 0 && allow_fallback && key >= SCC_SPRITE_START && key <= SCC_SPRITE_END) {
294 return this->parent->MapCharToGlyph(key);
297 return glyph;
301 * Free everything allocated w.r.t. freetype.
303 void UninitFreeType()
305 FT_Done_FreeType(_library);
306 _library = nullptr;
309 #if !defined(WITH_FONTCONFIG)
311 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) { return FT_Err_Cannot_Open_Resource; }
313 #endif /* !defined(WITH_FONTCONFIG) */
315 #endif /* WITH_FREETYPE */