diff --git a/src/fontcache/freetypefontcache.cpp b/src/fontcache/freetypefontcache.cpp
new file mode 100644
--- /dev/null
+++ b/src/fontcache/freetypefontcache.cpp
@@ -0,0 +1,330 @@
+/*
+ * 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 .
+ */
+
+/** @file freetypefontcache.cpp FreeType font cache implementation. */
+
+#include "../stdafx.h"
+#include "../debug.h"
+#include "../fontcache.h"
+#include "../fontdetection.h"
+#include "../blitter/factory.hpp"
+#include "../core/math_func.hpp"
+#include "../zoom_func.h"
+#include "../fileio_func.h"
+#include "truetypefontcache.h"
+
+#include "../table/control_codes.h"
+
+#include "../safeguards.h"
+
+#ifdef WITH_FREETYPE
+#include
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+#include FT_TRUETYPE_TABLES_H
+
+/** Font cache for fonts that are based on a freetype font. */
+class FreeTypeFontCache : public TrueTypeFontCache {
+private:
+ FT_Face face; ///< The font face associated with this font.
+
+ void SetFontSize(FontSize fs, FT_Face face, int pixels);
+ virtual const void *InternalGetFontTable(uint32 tag, size_t &length);
+ virtual const Sprite *InternalGetGlyph(GlyphID key, bool aa);
+
+public:
+ FreeTypeFontCache(FontSize fs, FT_Face face, int pixels);
+ ~FreeTypeFontCache();
+ virtual void ClearFontCache();
+ virtual GlyphID MapCharToGlyph(WChar key);
+ virtual const char *GetFontName() { return face->family_name; }
+ virtual bool IsBuiltInFont() { return false; }
+};
+
+FT_Library _library = nullptr;
+
+
+/**
+ * Create a new FreeTypeFontCache.
+ * @param fs The font size that is going to be cached.
+ * @param face The font that has to be loaded.
+ * @param pixels The number of pixels this font should be high.
+ */
+FreeTypeFontCache::FreeTypeFontCache(FontSize fs, FT_Face face, int pixels) : TrueTypeFontCache(fs, pixels), face(face)
+{
+ assert(face != nullptr);
+
+ this->SetFontSize(fs, face, pixels);
+}
+
+void FreeTypeFontCache::SetFontSize(FontSize fs, FT_Face face, int pixels)
+{
+ if (pixels == 0) {
+ /* Try to determine a good height based on the minimal height recommended by the font. */
+ int scaled_height = ScaleFontTrad(this->GetDefaultFontHeight(this->fs));
+ pixels = scaled_height;
+
+ TT_Header *head = (TT_Header *)FT_Get_Sfnt_Table(this->face, ft_sfnt_head);
+ if (head != nullptr) {
+ /* Font height is minimum height plus the difference between the default
+ * height for this font size and the small size. */
+ int diff = scaled_height - ScaleFontTrad(this->GetDefaultFontHeight(FS_SMALL));
+ pixels = Clamp(std::min(head->Lowest_Rec_PPEM, MAX_FONT_MIN_REC_SIZE) + diff, scaled_height, MAX_FONT_SIZE);
+ }
+ } else {
+ pixels = ScaleFontTrad(pixels);
+ }
+ this->used_size = pixels;
+
+ FT_Error err = FT_Set_Pixel_Sizes(this->face, 0, pixels);
+ if (err != FT_Err_Ok) {
+
+ /* Find nearest size to that requested */
+ FT_Bitmap_Size *bs = this->face->available_sizes;
+ int i = this->face->num_fixed_sizes;
+ if (i > 0) { // In pathetic cases one might get no fixed sizes at all.
+ int n = bs->height;
+ FT_Int chosen = 0;
+ for (; --i; bs++) {
+ if (abs(pixels - bs->height) >= abs(pixels - n)) continue;
+ n = bs->height;
+ chosen = this->face->num_fixed_sizes - i;
+ }
+
+ /* Don't use FT_Set_Pixel_Sizes here - it might give us another
+ * error, even though the size is available (FS#5885). */
+ err = FT_Select_Size(this->face, chosen);
+ }
+ }
+
+ if (err == FT_Err_Ok) {
+ this->units_per_em = this->face->units_per_EM;
+ this->ascender = this->face->size->metrics.ascender >> 6;
+ this->descender = this->face->size->metrics.descender >> 6;
+ this->height = this->ascender - this->descender;
+ } else {
+ /* Both FT_Set_Pixel_Sizes and FT_Select_Size failed. */
+ Debug(fontcache, 0, "Font size selection failed. Using FontCache defaults.");
+ }
+}
+
+/**
+ * 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 = nullptr;
+ switch (fs) {
+ default: NOT_REACHED();
+ case FS_SMALL: settings = &_fcsettings.small; break;
+ case FS_NORMAL: settings = &_fcsettings.medium; break;
+ case FS_LARGE: settings = &_fcsettings.large; break;
+ case FS_MONO: settings = &_fcsettings.mono; break;
+ }
+
+ if (settings->font.empty()) return;
+
+ if (_library == nullptr) {
+ if (FT_Init_FreeType(&_library) != FT_Err_Ok) {
+ ShowInfoF("Unable to initialize FreeType, using sprite fonts instead");
+ return;
+ }
+
+ Debug(fontcache, 2, "Initialized");
+ }
+
+ const char *font_name = settings->font.c_str();
+ FT_Face face = nullptr;
+
+ /* If font is an absolute path to a ttf, try loading that first. */
+ FT_Error error = FT_New_Face(_library, font_name, 0, &face);
+
+#if defined(WITH_COCOA)
+ extern void MacOSRegisterExternalFont(const char *file_path);
+ if (error == FT_Err_Ok) MacOSRegisterExternalFont(font_name);
+#endif
+
+ 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_name);
+ if (!full_font.empty()) {
+ error = FT_New_Face(_library, full_font.c_str(), 0, &face);
+#if defined(WITH_COCOA)
+ if (error == FT_Err_Ok) MacOSRegisterExternalFont(full_font.c_str());
+#endif
+ }
+ }
+
+ /* Try loading based on font face name (OS-wide fonts). */
+ if (error != FT_Err_Ok) error = GetFontByFaceName(font_name, &face);
+
+ if (error == FT_Err_Ok) {
+ Debug(fontcache, 2, "Requested '{}', using '{} {}'", font_name, face->family_name, face->style_name);
+
+ /* Attempt to select the unicode character map */
+ 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);
+
+ static const char *SIZE_TO_NAME[] = { "medium", "small", "large", "mono" };
+ ShowInfoF("Unable to use '%s' for %s font, FreeType reported error 0x%X, using sprite font instead", font_name, SIZE_TO_NAME[fs], error);
+ return;
+
+found_face:
+ new FreeTypeFontCache(fs, face, settings->size);
+}
+
+
+/**
+ * Free everything that was allocated for this font cache.
+ */
+FreeTypeFontCache::~FreeTypeFontCache()
+{
+ FT_Done_Face(this->face);
+ this->face = nullptr;
+ this->ClearFontCache();
+}
+
+/**
+ * Reset cached glyphs.
+ */
+void FreeTypeFontCache::ClearFontCache()
+{
+ /* Font scaling might have changed, determine font size anew if it was automatically selected. */
+ if (this->face != nullptr) this->SetFontSize(this->fs, this->face, this->req_size);
+
+ this->TrueTypeFontCache::ClearFontCache();
+}
+
+
+const Sprite *FreeTypeFontCache::InternalGetGlyph(GlyphID key, bool aa)
+{
+ FT_GlyphSlot slot = this->face->glyph;
+
+ FT_Load_Glyph(this->face, key, aa ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO);
+ FT_Render_Glyph(this->face->glyph, aa ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
+
+ /* Despite requesting a normal glyph, FreeType may have returned a bitmap */
+ aa = (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY);
+
+ /* Add 1 pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel */
+ uint width = std::max(1U, (uint)slot->bitmap.width + (this->fs == FS_NORMAL));
+ uint height = std::max(1U, (uint)slot->bitmap.rows + (this->fs == FS_NORMAL));
+
+ /* Limit glyph size to prevent overflows later on. */
+ if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) usererror("Font glyph is too large");
+
+ /* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it */
+ SpriteLoader::Sprite sprite;
+ sprite.AllocateData(ZOOM_LVL_NORMAL, width * height);
+ sprite.type = ST_FONT;
+ sprite.colours = (aa ? SCC_PAL | SCC_ALPHA : SCC_PAL);
+ sprite.width = width;
+ sprite.height = height;
+ sprite.x_offs = slot->bitmap_left;
+ sprite.y_offs = this->ascender - slot->bitmap_top;
+
+ /* Draw shadow for medium size */
+ if (this->fs == FS_NORMAL && !aa) {
+ for (uint y = 0; y < (uint)slot->bitmap.rows; y++) {
+ for (uint x = 0; x < (uint)slot->bitmap.width; x++) {
+ if (HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
+ sprite.data[1 + x + (1 + y) * sprite.width].m = SHADOW_COLOUR;
+ sprite.data[1 + x + (1 + y) * sprite.width].a = 0xFF;
+ }
+ }
+ }
+ }
+
+ for (uint y = 0; y < (uint)slot->bitmap.rows; y++) {
+ for (uint x = 0; x < (uint)slot->bitmap.width; x++) {
+ if (aa ? (slot->bitmap.buffer[x + y * slot->bitmap.pitch] > 0) : HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
+ sprite.data[x + y * sprite.width].m = FACE_COLOUR;
+ sprite.data[x + y * sprite.width].a = aa ? slot->bitmap.buffer[x + y * slot->bitmap.pitch] : 0xFF;
+ }
+ }
+ }
+
+ GlyphEntry new_glyph;
+ new_glyph.sprite = BlitterFactory::GetCurrentBlitter()->Encode(&sprite, SimpleSpriteAlloc);
+ new_glyph.width = slot->advance.x >> 6;
+
+ this->SetGlyphPtr(key, &new_glyph);
+
+ return new_glyph.sprite;
+}
+
+
+GlyphID FreeTypeFontCache::MapCharToGlyph(WChar key)
+{
+ assert(IsPrintable(key));
+
+ if (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END) {
+ return this->parent->MapCharToGlyph(key);
+ }
+
+ return FT_Get_Char_Index(this->face, key);
+}
+
+const void *FreeTypeFontCache::InternalGetFontTable(uint32 tag, size_t &length)
+{
+ FT_ULong len = 0;
+ FT_Byte *result = nullptr;
+
+ FT_Load_Sfnt_Table(this->face, tag, 0, nullptr, &len);
+
+ if (len > 0) {
+ result = MallocT(len);
+ FT_Load_Sfnt_Table(this->face, tag, 0, result, &len);
+ }
+
+ length = len;
+ return result;
+}
+
+/**
+ * Free everything allocated w.r.t. freetype.
+ */
+void UninitFreeType()
+{
+ FT_Done_FreeType(_library);
+ _library = nullptr;
+}
+
+#if !defined(_WIN32) && !defined(__APPLE__) && !defined(WITH_FONTCONFIG) && !defined(WITH_COCOA)
+
+FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) { return FT_Err_Cannot_Open_Resource; }
+
+#endif /* !defined(_WIN32) && !defined(__APPLE__) && !defined(WITH_FONTCONFIG) && !defined(WITH_COCOA) */
+
+#endif /* WITH_FREETYPE */