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/>.
8 /** @file freetypefontcache.cpp FreeType font cache implementation. */
10 #include "../stdafx.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"
27 #include FT_FREETYPE_H
29 #include FT_TRUETYPE_TABLES_H
31 /** Font cache for fonts that are based on a freetype font. */
32 class FreeTypeFontCache
: public TrueTypeFontCache
{
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
;
40 FreeTypeFontCache(FontSize fs
, FT_Face face
, int pixels
);
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;
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
)
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
);
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.
95 if (abs(pixels
- bs
->height
) >= abs(pixels
- n
)) continue;
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
;
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];
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) {
138 if (found
!= nullptr) {
139 error
= FT_Set_Charmap(face
, found
);
140 if (error
== FT_Err_Ok
) goto found_face
;
148 new FreeTypeFontCache(fs
, face
, size
);
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");
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. */
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
);
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
);
301 * Free everything allocated w.r.t. freetype.
303 void UninitFreeType()
305 FT_Done_FreeType(_library
);
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 */