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 "truetypefontcache.h"
20 #include "../table/control_codes.h"
22 #include "../safeguards.h"
26 #include FT_FREETYPE_H
28 #include FT_TRUETYPE_TABLES_H
30 /** Font cache for fonts that are based on a freetype font. */
31 class FreeTypeFontCache
: public TrueTypeFontCache
{
33 FT_Face face
; ///< The font face associated with this font.
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
);
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; }
48 FT_Library _library
= nullptr;
52 * Create a new FreeTypeFontCache.
53 * @param fs The font size that is going to be cached.
54 * @param face The font that has to be loaded.
55 * @param pixels The number of pixels this font should be high.
57 FreeTypeFontCache::FreeTypeFontCache(FontSize fs
, FT_Face face
, int pixels
) : TrueTypeFontCache(fs
, pixels
), face(face
)
59 assert(face
!= nullptr);
61 this->SetFontSize(fs
, face
, pixels
);
64 void FreeTypeFontCache::SetFontSize(FontSize fs
, FT_Face face
, int pixels
)
67 /* Try to determine a good height based on the minimal height recommended by the font. */
68 int scaled_height
= ScaleFontTrad(this->GetDefaultFontHeight(this->fs
));
69 pixels
= scaled_height
;
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
- ScaleFontTrad(this->GetDefaultFontHeight(FS_SMALL
));
76 pixels
= Clamp(std::min
<uint
>(head
->Lowest_Rec_PPEM
, MAX_FONT_MIN_REC_SIZE
) + diff
, scaled_height
, MAX_FONT_SIZE
);
79 pixels
= ScaleFontTrad(pixels
);
81 this->used_size
= pixels
;
83 FT_Error err
= FT_Set_Pixel_Sizes(this->face
, 0, pixels
);
84 if (err
!= FT_Err_Ok
) {
86 /* Find nearest size to that requested */
87 FT_Bitmap_Size
*bs
= this->face
->available_sizes
;
88 int i
= this->face
->num_fixed_sizes
;
89 if (i
> 0) { // In pathetic cases one might get no fixed sizes at all.
93 if (abs(pixels
- bs
->height
) >= abs(pixels
- n
)) continue;
95 chosen
= this->face
->num_fixed_sizes
- i
;
98 /* Don't use FT_Set_Pixel_Sizes here - it might give us another
99 * error, even though the size is available (FS#5885). */
100 err
= FT_Select_Size(this->face
, chosen
);
104 if (err
== FT_Err_Ok
) {
105 this->units_per_em
= this->face
->units_per_EM
;
106 this->ascender
= this->face
->size
->metrics
.ascender
>> 6;
107 this->descender
= this->face
->size
->metrics
.descender
>> 6;
108 this->height
= this->ascender
- this->descender
;
110 /* Both FT_Set_Pixel_Sizes and FT_Select_Size failed. */
111 Debug(fontcache
, 0, "Font size selection failed. Using FontCache defaults.");
116 * Loads the freetype font.
117 * First type to load the fontname as if it were a path. If that fails,
118 * try to resolve the filename of the font using fontconfig, where the
119 * format is 'font family name' or 'font family name, font style'.
120 * @param fs The font size to load.
122 void LoadFreeTypeFont(FontSize fs
)
124 FontCacheSubSetting
*settings
= nullptr;
126 default: NOT_REACHED();
127 case FS_SMALL
: settings
= &_fcsettings
.small
; break;
128 case FS_NORMAL
: settings
= &_fcsettings
.medium
; break;
129 case FS_LARGE
: settings
= &_fcsettings
.large
; break;
130 case FS_MONO
: settings
= &_fcsettings
.mono
; break;
133 if (settings
->font
.empty()) return;
135 if (_library
== nullptr) {
136 if (FT_Init_FreeType(&_library
) != FT_Err_Ok
) {
137 ShowInfoF("Unable to initialize FreeType, using sprite fonts instead");
141 Debug(fontcache
, 2, "Initialized");
144 const char *font_name
= settings
->font
.c_str();
145 FT_Face face
= nullptr;
147 /* If font is an absolute path to a ttf, try loading that first. */
148 FT_Error error
= FT_New_Face(_library
, font_name
, 0, &face
);
150 #if defined(WITH_COCOA)
151 extern void MacOSRegisterExternalFont(const char *file_path
);
152 if (error
== FT_Err_Ok
) MacOSRegisterExternalFont(font_name
);
155 if (error
!= FT_Err_Ok
) {
156 /* Check if font is a relative filename in one of our search-paths. */
157 std::string full_font
= FioFindFullPath(BASE_DIR
, font_name
);
158 if (!full_font
.empty()) {
159 error
= FT_New_Face(_library
, full_font
.c_str(), 0, &face
);
160 #if defined(WITH_COCOA)
161 if (error
== FT_Err_Ok
) MacOSRegisterExternalFont(full_font
.c_str());
166 /* Try loading based on font face name (OS-wide fonts). */
167 if (error
!= FT_Err_Ok
) error
= GetFontByFaceName(font_name
, &face
);
169 if (error
== FT_Err_Ok
) {
170 Debug(fontcache
, 2, "Requested '{}', using '{} {}'", font_name
, face
->family_name
, face
->style_name
);
172 /* Attempt to select the unicode character map */
173 error
= FT_Select_Charmap(face
, ft_encoding_unicode
);
174 if (error
== FT_Err_Ok
) goto found_face
; // Success
176 if (error
== FT_Err_Invalid_CharMap_Handle
) {
177 /* Try to pick a different character map instead. We default to
178 * the first map, but platform_id 0 encoding_id 0 should also
179 * be unicode (strange system...) */
180 FT_CharMap found
= face
->charmaps
[0];
183 for (i
= 0; i
< face
->num_charmaps
; i
++) {
184 FT_CharMap charmap
= face
->charmaps
[i
];
185 if (charmap
->platform_id
== 0 && charmap
->encoding_id
== 0) {
190 if (found
!= nullptr) {
191 error
= FT_Set_Charmap(face
, found
);
192 if (error
== FT_Err_Ok
) goto found_face
;
199 static const char *SIZE_TO_NAME
[] = { "medium", "small", "large", "mono" };
200 ShowInfoF("Unable to use '%s' for %s font, FreeType reported error 0x%X, using sprite font instead", font_name
, SIZE_TO_NAME
[fs
], error
);
204 new FreeTypeFontCache(fs
, face
, settings
->size
);
209 * Free everything that was allocated for this font cache.
211 FreeTypeFontCache::~FreeTypeFontCache()
213 FT_Done_Face(this->face
);
214 this->face
= nullptr;
215 this->ClearFontCache();
219 * Reset cached glyphs.
221 void FreeTypeFontCache::ClearFontCache()
223 /* Font scaling might have changed, determine font size anew if it was automatically selected. */
224 if (this->face
!= nullptr) this->SetFontSize(this->fs
, this->face
, this->req_size
);
226 this->TrueTypeFontCache::ClearFontCache();
230 const Sprite
*FreeTypeFontCache::InternalGetGlyph(GlyphID key
, bool aa
)
232 FT_GlyphSlot slot
= this->face
->glyph
;
234 FT_Load_Glyph(this->face
, key
, aa
? FT_LOAD_TARGET_NORMAL
: FT_LOAD_TARGET_MONO
);
235 FT_Render_Glyph(this->face
->glyph
, aa
? FT_RENDER_MODE_NORMAL
: FT_RENDER_MODE_MONO
);
237 /* Despite requesting a normal glyph, FreeType may have returned a bitmap */
238 aa
= (slot
->bitmap
.pixel_mode
== FT_PIXEL_MODE_GRAY
);
240 /* Add 1 pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel */
241 uint width
= std::max(1U, (uint
)slot
->bitmap
.width
+ (this->fs
== FS_NORMAL
));
242 uint height
= std::max(1U, (uint
)slot
->bitmap
.rows
+ (this->fs
== FS_NORMAL
));
244 /* Limit glyph size to prevent overflows later on. */
245 if (width
> MAX_GLYPH_DIM
|| height
> MAX_GLYPH_DIM
) usererror("Font glyph is too large");
247 /* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it */
248 SpriteLoader::Sprite sprite
;
249 sprite
.AllocateData(ZOOM_LVL_NORMAL
, width
* height
);
250 sprite
.type
= ST_FONT
;
251 sprite
.colours
= (aa
? SCC_PAL
| SCC_ALPHA
: SCC_PAL
);
252 sprite
.width
= width
;
253 sprite
.height
= height
;
254 sprite
.x_offs
= slot
->bitmap_left
;
255 sprite
.y_offs
= this->ascender
- slot
->bitmap_top
;
257 /* Draw shadow for medium size */
258 if (this->fs
== FS_NORMAL
&& !aa
) {
259 for (uint y
= 0; y
< (uint
)slot
->bitmap
.rows
; y
++) {
260 for (uint x
= 0; x
< (uint
)slot
->bitmap
.width
; x
++) {
261 if (HasBit(slot
->bitmap
.buffer
[(x
/ 8) + y
* slot
->bitmap
.pitch
], 7 - (x
% 8))) {
262 sprite
.data
[1 + x
+ (1 + y
) * sprite
.width
].m
= SHADOW_COLOUR
;
263 sprite
.data
[1 + x
+ (1 + y
) * sprite
.width
].a
= 0xFF;
269 for (uint y
= 0; y
< (uint
)slot
->bitmap
.rows
; y
++) {
270 for (uint x
= 0; x
< (uint
)slot
->bitmap
.width
; x
++) {
271 if (aa
? (slot
->bitmap
.buffer
[x
+ y
* slot
->bitmap
.pitch
] > 0) : HasBit(slot
->bitmap
.buffer
[(x
/ 8) + y
* slot
->bitmap
.pitch
], 7 - (x
% 8))) {
272 sprite
.data
[x
+ y
* sprite
.width
].m
= FACE_COLOUR
;
273 sprite
.data
[x
+ y
* sprite
.width
].a
= aa
? slot
->bitmap
.buffer
[x
+ y
* slot
->bitmap
.pitch
] : 0xFF;
278 GlyphEntry new_glyph
;
279 new_glyph
.sprite
= BlitterFactory::GetCurrentBlitter()->Encode(&sprite
, SimpleSpriteAlloc
);
280 new_glyph
.width
= slot
->advance
.x
>> 6;
282 this->SetGlyphPtr(key
, &new_glyph
);
284 return new_glyph
.sprite
;
288 GlyphID
FreeTypeFontCache::MapCharToGlyph(WChar key
)
290 assert(IsPrintable(key
));
292 if (key
>= SCC_SPRITE_START
&& key
<= SCC_SPRITE_END
) {
293 return this->parent
->MapCharToGlyph(key
);
296 return FT_Get_Char_Index(this->face
, key
);
299 const void *FreeTypeFontCache::InternalGetFontTable(uint32 tag
, size_t &length
)
302 FT_Byte
*result
= nullptr;
304 FT_Load_Sfnt_Table(this->face
, tag
, 0, nullptr, &len
);
307 result
= MallocT
<FT_Byte
>(len
);
308 FT_Load_Sfnt_Table(this->face
, tag
, 0, result
, &len
);
316 * Free everything allocated w.r.t. freetype.
318 void UninitFreeType()
320 FT_Done_FreeType(_library
);
324 #if !defined(_WIN32) && !defined(__APPLE__) && !defined(WITH_FONTCONFIG) && !defined(WITH_COCOA)
326 FT_Error
GetFontByFaceName(const char *font_name
, FT_Face
*face
) { return FT_Err_Cannot_Open_Resource
; }
328 #endif /* !defined(_WIN32) && !defined(__APPLE__) && !defined(WITH_FONTCONFIG) && !defined(WITH_COCOA) */
330 #endif /* WITH_FREETYPE */