10#include "../../stdafx.h"
11#include "../../debug.h"
12#include "../../blitter/factory.hpp"
13#include "../../core/math_func.hpp"
14#include "../../error_func.h"
15#include "../../fileio_func.h"
16#include "../../fontcache.h"
17#include "../../fontcache/truetypefontcache.h"
18#include "../../library_loader.h"
19#include "../../string_func.h"
20#include "../../strings_func.h"
21#include "../../zoom_func.h"
24#include "../../table/control_codes.h"
31#include "../../safeguards.h"
34 LOCALESIGNATURE locale;
37 std::vector<std::wstring> fonts;
39 bool Add(
const std::wstring_view &font)
41 auto it = std::ranges::find(this->fonts, font);
42 if (it != std::end(this->fonts))
return false;
44 this->fonts.emplace_back(font);
50static int CALLBACK EnumFontCallback(
const ENUMLOGFONTEX *logfont,
const NEWTEXTMETRICEX *metric, DWORD type, LPARAM lParam)
55 if (!info->Add(logfont->elfFullName))
return 1;
57 if (!(type & TRUETYPE_FONTTYPE))
return 1;
59 if (logfont->elfLogFont.lfCharSet == SYMBOL_CHARSET)
return 1;
61 if (info->fontsizes.
Test(
FS_MONO) && (logfont->elfLogFont.lfPitchAndFamily & (FF_MODERN | FIXED_PITCH)) != (FF_MODERN | FIXED_PITCH))
return 1;
64 auto check_bitfields = [&]() {
66 for (uint8_t i = 0; i < 4; i++) {
67 if ((metric->ntmFontSig.fsUsb[i] & info->locale.lsUsb[i]) != 0)
return true;
70 for (uint8_t i = 0; i < 2; i++) {
71 if ((metric->ntmFontSig.fsCsb[i] & info->locale.lsCsbSupported[i]) != 0)
return true;
75 if (!check_bitfields())
return 1;
77 char font_name[MAX_PATH];
82 Debug(fontcache, 1,
"Fallback font: {}", font_name);
94 this->
dc = CreateCompatibleDC(
nullptr);
95 this->SetFontSize(pixels);
98Win32FontCache::~Win32FontCache()
102 DeleteObject(this->
font);
105void Win32FontCache::SetFontSize(
int pixels)
109 int scaled_height =
ScaleGUITrad(FontCache::GetDefaultFontHeight(this->
fs));
110 pixels = scaled_height;
112 HFONT temp = CreateFontIndirect(&this->logfont);
113 if (temp !=
nullptr) {
114 HGDIOBJ old = SelectObject(this->
dc, temp);
116 UINT size = GetOutlineTextMetrics(this->
dc, 0,
nullptr);
117 LPOUTLINETEXTMETRIC otm = (LPOUTLINETEXTMETRIC)
new BYTE[size];
118 GetOutlineTextMetrics(this->
dc, size, otm);
127 SelectObject(
dc, old);
136 this->logfont.lfHeight = -pixels;
137 this->logfont.lfWidth = 0;
138 this->logfont.lfOutPrecision = OUT_TT_ONLY_PRECIS;
139 this->logfont.lfQuality = ANTIALIASED_QUALITY;
141 if (this->
font !=
nullptr) {
143 DeleteObject(this->
font);
145 this->
font = CreateFontIndirect(&this->logfont);
149 UINT otmSize = GetOutlineTextMetrics(this->
dc, 0,
nullptr);
150 POUTLINETEXTMETRIC otm = (POUTLINETEXTMETRIC)
new BYTE[otmSize];
151 GetOutlineTextMetrics(this->
dc, otmSize, otm);
153 this->
ascender = otm->otmTextMetrics.tmAscent;
154 this->
descender = otm->otmTextMetrics.tmDescent;
156 this->
glyph_size.cx = otm->otmTextMetrics.tmMaxCharWidth;
157 this->
glyph_size.cy = otm->otmTextMetrics.tmHeight;
159 this->
fontname =
FS2OTTD((LPWSTR)((BYTE *)otm + (ptrdiff_t)otm->otmpFaceName));
161 Debug(fontcache, 2,
"Win32FontCache: Loaded font '{}' with size {}", this->
fontname, pixels);
171 if (this->
font !=
nullptr) this->SetFontSize(this->
req_size);
176 const Sprite *Win32FontCache::InternalGetGlyph(
GlyphID key,
bool aa)
179 MAT2 mat = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
182 DWORD size = GetGlyphOutline(this->
dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, 0, nullptr, &mat);
183 if (size == GDI_ERROR) UserError(
"Unable to render font glyph");
187 uint width = std::max(1U, (uint)gm.gmBlackBoxX + shadow);
188 uint
height = std::max(1U, (uint)gm.gmBlackBoxY + shadow);
195 GetGlyphOutline(this->
dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, size, bmp, &mat);
203 sprite.
width = width;
205 sprite.
x_offs = gm.gmptGlyphOrigin.x;
214 uint pitch =
Align(aa ? gm.gmBlackBoxX : std::max((gm.gmBlackBoxX + 7u) / 8u, 1u), 4);
218 for (uint y = 0; y < gm.gmBlackBoxY; y++) {
219 for (uint x = 0; x < gm.gmBlackBoxX; x++) {
220 if (aa ? (bmp[x + y * pitch] > 0) :
HasBit(bmp[(x / 8) + y * pitch], 7 - (x % 8))) {
221 sprite.
data[shadow + x + (shadow + y) * sprite.
width].
m = SHADOW_COLOUR;
222 sprite.
data[shadow + x + (shadow + y) * sprite.
width].
a = aa ? (bmp[x + y * pitch] << 2) - 1 : 0xFF;
228 for (uint y = 0; y < gm.gmBlackBoxY; y++) {
229 for (uint x = 0; x < gm.gmBlackBoxX; x++) {
230 if (aa ? (bmp[x + y * pitch] > 0) :
HasBit(bmp[(x / 8) + y * pitch], 7 - (x % 8))) {
231 sprite.
data[x + y * sprite.
width].
m = FACE_COLOUR;
232 sprite.
data[x + y * sprite.
width].
a = aa ? (bmp[x + y * pitch] << 2) - 1 : 0xFF;
241 GlyphEntry new_glyph;
242 new_glyph.
data = std::move(allocator.data);
243 new_glyph.width = gm.gmCellIncX;
245 return this->SetGlyphPtr(key, std::move(new_glyph)).GetSprite();
250 assert(IsPrintable(key));
254 if (key >= 0x010000U) {
255 chars[0] = (wchar_t)(((key - 0x010000U) >> 10) + 0xD800);
256 chars[1] = (wchar_t)(((key - 0x010000U) & 0x3FF) + 0xDC00);
258 chars[0] = (wchar_t)(key & 0xFFFF);
261 WORD glyphs[2] = { 0, 0 };
262 GetGlyphIndicesW(this->
dc, chars, key >= 0x010000U ? 2 : 1, glyphs, GGI_MARK_NONEXISTING_GLYPHS);
264 if (glyphs[0] != 0xFFFF)
return glyphs[0];
278 std::unique_ptr<FontCache>
LoadFont(
FontSize fs,
FontType fonttype,
bool search,
const std::string &font, std::span<const std::byte> os_handle)
const override
283 logfont.lfPitchAndFamily = fs ==
FS_MONO ? FIXED_PITCH : VARIABLE_PITCH;
284 logfont.lfCharSet = DEFAULT_CHARSET;
285 logfont.lfOutPrecision = OUT_OUTLINE_PRECIS;
286 logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
288 if (!os_handle.empty()) {
289 logfont = *
reinterpret_cast<const LOGFONT *
>(os_handle.data());
290 }
else if (font.find(
'.') != std::string::npos) {
292 if (!TryLoadFontFromFile(font, logfont)) {
293 ShowInfo(
"Unable to load file '{}' for {} font, using default windows font selection instead", font, FontSizeToName(fs));
294 if (!search)
return nullptr;
298 if (logfont.lfFaceName[0] == 0) {
308 Debug(fontcache, 1,
"Trying fallback fonts");
310 std::wstring lang =
OTTD2FS(language_isocode.substr(0, language_isocode.find(
'_')));
311 if (GetLocaleInfoEx(lang.c_str(), LOCALE_FONTSIGNATURE,
reinterpret_cast<LPWSTR
>(&langInfo.locale),
sizeof(langInfo.locale) /
sizeof(
wchar_t)) == 0) {
313 Debug(fontcache, 1,
"Can't get locale info for fallback font (isocode={})", language_isocode);
316 langInfo.fontsizes = fontsizes;
317 langInfo.callback = callback;
321 font.lfCharSet = DEFAULT_CHARSET;
322 font.lfFaceName[0] =
'\0';
323 font.lfPitchAndFamily = 0;
325 HDC dc = GetDC(
nullptr);
326 int ret = EnumFontFamiliesEx(dc, &font, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&langInfo, 0);
327 ReleaseDC(
nullptr, dc);
332 static std::unique_ptr<FontCache> LoadWin32Font(
FontSize fs,
const LOGFONT &logfont, uint size, std::string_view font_name)
334 HFONT font = CreateFontIndirect(&logfont);
335 if (font ==
nullptr) {
336 ShowInfo(
"Unable to use '{}' for {} font, Win32 reported error 0x{:X}, using sprite font instead", font_name, FontSizeToName(fs), GetLastError());
341 return std::make_unique<Win32FontCache>(fs, logfont, size);
344 static bool TryLoadFontFromFile(
const std::string &font_name, LOGFONT &logfont)
346 wchar_t fontPath[MAX_PATH] = {};
354 if (!full_font.empty()) {
359 if (fontPath[0] != 0) {
360 if (AddFontResourceEx(fontPath, FR_PRIVATE, 0) != 0) {
364 typedef BOOL(WINAPI *PFNGETFONTRESOURCEINFO)(LPCTSTR, LPDWORD, LPVOID, DWORD);
365 static PFNGETFONTRESOURCEINFO GetFontResourceInfo = _gdi32.GetFunction(
"GetFontResourceInfoW");
367 if (GetFontResourceInfo !=
nullptr) {
370 if (GetFontResourceInfo(fontPath, &len,
nullptr, 2) && len >=
sizeof(LOGFONT)) {
371 LOGFONT *buf = (LOGFONT *)
new uint8_t[len];
372 if (GetFontResourceInfo(fontPath, &len, buf, 2)) {
375 delete[](uint8_t *)buf;
380 if (logfont.lfFaceName[0] == 0) {
381 wchar_t fname[_MAX_FNAME];
382 _wsplitpath(fontPath,
nullptr,
nullptr, fname,
nullptr);
384 wcsncpy_s(logfont.lfFaceName,
lengthof(logfont.lfFaceName), fname, _TRUNCATE);
390 return logfont.lfFaceName[0] != 0;
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr bool None() const
Test if none of the values are set.
constexpr Timpl & Set()
Set all bits.
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
int height
The height of the font.
static void AddFallbackWithHandle(FontSizes fontsizes, FontLoadReason load_reason, std::string_view name, T &handle)
Add a fallback font, with OS-specific handle.
const FontSize fs
The size of the font.
int descender
The descender value of the font.
int ascender
The ascender value of the font.
A searcher for missing glyphs.
FontSizes FindMissingGlyphs()
Test if any glyphs are missing.
T * Allocate(size_t count)
Get buffer of at least count times T.
virtual Sprite * Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator)=0
Convert a sprite from the loader to our own format.
Font cache for fonts that are based on a TrueType font.
static constexpr int MAX_GLYPH_DIM
Maximum glyph dimensions.
int used_size
Used font size.
int req_size
Requested font size.
void ClearFontCache() override
Reset cached glyphs.
static constexpr uint MAX_FONT_MIN_REC_SIZE
Upper limit for the recommended font size in case a font file contains nonsensical values.
SpriteAllocator that allocates memory via a unique_ptr array.
std::unique_ptr< FontCache > LoadFont(FontSize fs, FontType fonttype, bool search, const std::string &font, std::span< const std::byte > os_handle) const override
Loads the GDI font.
HGDIOBJ old_font
Old font selected into the GDI context.
std::string fontname
Cached copy of loaded font facename.
HFONT font
The font face associated with this font.
HDC dc
Cached GDI device context.
void ClearFontCache() override
Reset cached glyphs.
GlyphID MapCharToGlyph(char32_t key) override
Map a character into a glyph.
SIZE glyph_size
Maximum size of regular glyphs.
Win32FontCache(FontSize fs, const LOGFONT &logfont, int pixels)
Create a new Win32FontCache.
ReusableBuffer< uint8_t > render_buffer
Temporary buffer for rendering glyphs.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
bool FileExists(std::string_view filename)
Test whether the given filename exists.
std::string FioFindFullPath(Subdirectory subdir, std::string_view filename)
Find a path to the filename in one of the search directories.
@ BASE_DIR
Base directory for all subdirectories.
Functions related to font handling on Win32.
uint GetFontCacheFontSize(FontSize fs)
Get the scalable font size to use for a FontSize.
FontType
Different types of font that can be loaded.
@ TrueType
Scalable TrueType fonts.
uint32_t GlyphID
Glyphs are characters from a font.
@ Font
A sprite used for fonts.
FontSize
Available font sizes.
@ FS_MONO
Index of the monospaced font in the font tables.
@ FS_SMALL
Index of the small font in the font tables.
@ FS_NORMAL
Index of the normal font in the font tables.
constexpr T Align(const T x, uint n)
Return the smallest multiple of n equal or greater than x.
@ Palette
Sprite has palette data.
#define lengthof(array)
Return the length of an fixed size array.
bool StrContainsIgnoreCase(std::string_view str, std::string_view value)
Checks if a string is contained in another string, while ignoring the case of the characters.
Structure for passing information from the sprite loader to the blitter.
SpriteComponents colours
The colour components of the sprite with useful information.
void AllocateData(ZoomLevel zoom, size_t size)
Allocate the sprite data of this sprite.
uint16_t width
Width of the sprite.
int16_t x_offs
The x-offset of where the sprite will be drawn.
SpriteLoader::CommonPixel * data
The sprite itself.
uint16_t height
Height of the sprite.
int16_t y_offs
The y-offset of where the sprite will be drawn.
Data structure describing a sprite.
std::byte data[]
Sprite data.
static const int MAX_FONT_SIZE
Maximum font size.
std::string_view convert_from_fs(const std::wstring_view src, std::span< char > dst_buf)
Convert to OpenTTD's encoding from that of the environment in UNICODE.
std::wstring OTTD2FS(std::string_view name)
Convert from OpenTTD's encoding to a wide string.
std::string FS2OTTD(std::wstring_view name)
Convert to OpenTTD's encoding from a wide string.
wchar_t * convert_to_fs(std::string_view src, std::span< wchar_t > dst_buf)
Convert from OpenTTD's encoding to that of the environment in UNICODE.
declarations of functions for MS windows systems