diff options
author | Michael Lutz <michi@icosahedron.de> | 2021-02-13 17:34:22 +0100 |
---|---|---|
committer | Michael Lutz <michi@icosahedron.de> | 2021-02-13 20:09:14 +0100 |
commit | c6af8f16f63d878371d1a823c12fbf21423505b5 (patch) | |
tree | bd0e33995d016b99d39187f2da04ccbee847504f /src/fontcache.cpp | |
parent | 5ad16409847a17534389054a1b7f5fb83703fb4d (diff) | |
download | openttd-c6af8f16f63d878371d1a823c12fbf21423505b5.tar.xz |
Codechange: [Win32] Move Win32-specific font code to a seperate file.
Diffstat (limited to 'src/fontcache.cpp')
-rw-r--r-- | src/fontcache.cpp | 393 |
1 files changed, 10 insertions, 383 deletions
diff --git a/src/fontcache.cpp b/src/fontcache.cpp index 9cdc0b975..6cc25154f 100644 --- a/src/fontcache.cpp +++ b/src/fontcache.cpp @@ -9,6 +9,7 @@ #include "stdafx.h" #include "fontcache.h" +#include "fontcache_internal.h" #include "fontdetection.h" #include "blitter/factory.hpp" #include "core/math_func.hpp" @@ -31,6 +32,8 @@ static const int ASCII_LETTERSTART = 32; ///< First printable ASCII letter. static const int _default_font_height[FS_END] = {10, 6, 18, 10}; static const int _default_font_ascender[FS_END] = { 8, 5, 15, 8}; +FreeTypeSettings _freetype; + /** * Create a new font cache. * @param fs The size of the font. @@ -52,6 +55,11 @@ FontCache::~FontCache() Layouter::ResetFontCache(this->fs); } +int FontCache::GetDefaultFontHeight(FontSize fs) +{ + return _default_font_height[fs]; +} + /** * Get height of a character for a given font size. @@ -199,65 +207,6 @@ bool SpriteFontCache::GetDrawGlyphShadow() #if defined(WITH_FREETYPE) || defined(_WIN32) -FreeTypeSettings _freetype; - -static const int MAX_FONT_SIZE = 72; ///< Maximum font size. - -static const byte FACE_COLOUR = 1; -static const byte SHADOW_COLOUR = 2; - -/** Font cache for fonts that are based on a TrueType font. */ -class TrueTypeFontCache : public FontCache { -protected: - int req_size; ///< Requested font size. - int used_size; ///< Used font size. - - typedef SmallMap<uint32, std::pair<size_t, const void*> > FontTable; ///< Table with font table cache - FontTable font_tables; ///< Cached font tables. - - /** Container for information about a glyph. */ - struct GlyphEntry { - Sprite *sprite; ///< The loaded sprite. - byte width; ///< The width of the glyph. - bool duplicate; ///< Whether this glyph entry is a duplicate, i.e. may this be freed? - }; - - /** - * The glyph cache. This is structured to reduce memory consumption. - * 1) There is a 'segment' table for each font size. - * 2) Each segment table is a discrete block of characters. - * 3) Each block contains 256 (aligned) characters sequential characters. - * - * The cache is accessed in the following way: - * For character 0x0041 ('A'): glyph_to_sprite[0x00][0x41] - * For character 0x20AC (Euro): glyph_to_sprite[0x20][0xAC] - * - * Currently only 256 segments are allocated, "limiting" us to 65536 characters. - * This can be simply changed in the two functions Get & SetGlyphPtr. - */ - GlyphEntry **glyph_to_sprite; - - GlyphEntry *GetGlyphPtr(GlyphID key); - void SetGlyphPtr(GlyphID key, const GlyphEntry *glyph, bool duplicate = false); - - virtual const void *InternalGetFontTable(uint32 tag, size_t &length) = 0; - virtual const Sprite *InternalGetGlyph(GlyphID key, bool aa) = 0; - -public: - TrueTypeFontCache(FontSize fs, int pixels); - virtual ~TrueTypeFontCache(); - virtual int GetFontSize() const { return this->used_size; } - virtual SpriteID GetUnicodeGlyph(WChar key) { return this->parent->GetUnicodeGlyph(key); } - virtual void SetUnicodeGlyph(WChar key, SpriteID sprite) { this->parent->SetUnicodeGlyph(key, sprite); } - virtual void InitializeUnicodeGlyphMap() { this->parent->InitializeUnicodeGlyphMap(); } - virtual const Sprite *GetGlyph(GlyphID key); - virtual const void *GetFontTable(uint32 tag, size_t &length); - virtual void ClearFontCache(); - virtual uint GetGlyphWidth(GlyphID key); - virtual bool GetDrawGlyphShadow(); - virtual bool IsBuiltInFont() { return false; } -}; - /** * Create a new TrueTypeFontCache. * @param fs The font size that is going to be cached. @@ -329,7 +278,7 @@ void TrueTypeFontCache::SetGlyphPtr(GlyphID key, const GlyphEntry *glyph, bool d this->glyph_to_sprite[GB(key, 8, 8)][GB(key, 0, 8)].duplicate = duplicate; } -static void *AllocateFont(size_t size) +void *AllocateFont(size_t size) { return MallocT<byte>(size); } @@ -731,330 +680,7 @@ const void *FreeTypeFontCache::InternalGetFontTable(uint32 tag, size_t &length) #elif defined(_WIN32) -#include "os/windows/win32.h" -#ifndef ANTIALIASED_QUALITY -#define ANTIALIASED_QUALITY 4 -#endif - -/** Font cache for fonts that are based on a Win32 font. */ -class Win32FontCache : public TrueTypeFontCache { -private: - LOGFONT logfont; ///< Logical font information for selecting the font face. - HFONT font = nullptr; ///< The font face associated with this font. - HDC dc = nullptr; ///< Cached GDI device context. - HGDIOBJ old_font; ///< Old font selected into the GDI context. - SIZE glyph_size; ///< Maximum size of regular glyphs. - - void SetFontSize(FontSize fs, int pixels); - virtual const void *InternalGetFontTable(uint32 tag, size_t &length); - virtual const Sprite *InternalGetGlyph(GlyphID key, bool aa); - -public: - Win32FontCache(FontSize fs, const LOGFONT &logfont, int pixels); - ~Win32FontCache(); - virtual void ClearFontCache(); - virtual GlyphID MapCharToGlyph(WChar key); - virtual const char *GetFontName() { return WIDE_TO_MB(this->logfont.lfFaceName); } - virtual bool IsBuiltInFont() { return false; } - virtual const void *GetOSHandle() { return &this->logfont; } -}; - - -/** - * Create a new Win32FontCache. - * @param fs The font size that is going to be cached. - * @param logfont The font that has to be loaded. - * @param pixels The number of pixels this font should be high. - */ -Win32FontCache::Win32FontCache(FontSize fs, const LOGFONT &logfont, int pixels) : TrueTypeFontCache(fs, pixels), logfont(logfont) -{ - this->dc = CreateCompatibleDC(nullptr); - this->SetFontSize(fs, pixels); -} - -Win32FontCache::~Win32FontCache() -{ - this->ClearFontCache(); - DeleteDC(this->dc); - DeleteObject(this->font); -} -void Win32FontCache::SetFontSize(FontSize fs, int pixels) -{ - if (pixels == 0) { - /* Try to determine a good height based on the minimal height recommended by the font. */ - int scaled_height = ScaleFontTrad(_default_font_height[this->fs]); - pixels = scaled_height; - - HFONT temp = CreateFontIndirect(&this->logfont); - if (temp != nullptr) { - HGDIOBJ old = SelectObject(this->dc, temp); - - UINT size = GetOutlineTextMetrics(this->dc, 0, nullptr); - LPOUTLINETEXTMETRIC otm = (LPOUTLINETEXTMETRIC)AllocaM(BYTE, size); - GetOutlineTextMetrics(this->dc, size, otm); - - /* 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(_default_font_height[FS_SMALL]); - pixels = Clamp(std::min(otm->otmusMinimumPPEM, 20u) + diff, scaled_height, MAX_FONT_SIZE); - - SelectObject(dc, old); - DeleteObject(temp); - } - } else { - pixels = ScaleFontTrad(pixels); - } - this->used_size = pixels; - - /* Create GDI font handle. */ - this->logfont.lfHeight = -pixels; - this->logfont.lfWidth = 0; - this->logfont.lfOutPrecision = ANTIALIASED_QUALITY; - - if (this->font != nullptr) { - SelectObject(dc, this->old_font); - DeleteObject(this->font); - } - this->font = CreateFontIndirect(&this->logfont); - this->old_font = SelectObject(this->dc, this->font); - - /* Query the font metrics we needed. */ - UINT otmSize = GetOutlineTextMetrics(this->dc, 0, nullptr); - POUTLINETEXTMETRIC otm = (POUTLINETEXTMETRIC)AllocaM(BYTE, otmSize); - GetOutlineTextMetrics(this->dc, otmSize, otm); - - this->units_per_em = otm->otmEMSquare; - this->ascender = otm->otmTextMetrics.tmAscent; - this->descender = otm->otmTextMetrics.tmDescent; - this->height = this->ascender + this->descender; - this->glyph_size.cx = otm->otmTextMetrics.tmMaxCharWidth; - this->glyph_size.cy = otm->otmTextMetrics.tmHeight; - - DEBUG(freetype, 2, "Loaded font '%s' with size %d", FS2OTTD((LPTSTR)((BYTE *)otm + (ptrdiff_t)otm->otmpFullName)), pixels); -} - -/** - * Reset cached glyphs. - */ -void Win32FontCache::ClearFontCache() -{ - /* GUI scaling might have changed, determine font size anew if it was automatically selected. */ - if (this->font != nullptr) this->SetFontSize(this->fs, this->req_size); - - this->TrueTypeFontCache::ClearFontCache(); -} - -/* virtual */ const Sprite *Win32FontCache::InternalGetGlyph(GlyphID key, bool aa) -{ - GLYPHMETRICS gm; - MAT2 mat = { {0, 1}, {0, 0}, {0, 0}, {0, 1} }; - - /* Make a guess for the needed memory size. */ - DWORD size = this->glyph_size.cy * Align(aa ? this->glyph_size.cx : std::max(this->glyph_size.cx / 8l, 1l), 4); // Bitmap data is DWORD-aligned rows. - byte *bmp = AllocaM(byte, size); - size = GetGlyphOutline(this->dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, size, bmp, &mat); - - if (size == GDI_ERROR) { - /* No dice with the guess. First query size of needed glyph memory, then allocate the - * memory and query again. This dance is necessary as some glyphs will only render with - * the exact matching size; e.g. the space glyph has no pixels and must be requested - * with size == 0, anything else fails. Unfortunately, a failed call doesn't return any - * info about the size and thus the triple GetGlyphOutline()-call. */ - size = GetGlyphOutline(this->dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, 0, nullptr, &mat); - if (size == GDI_ERROR) usererror("Unable to render font glyph"); - bmp = AllocaM(byte, size); - GetGlyphOutline(this->dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, size, bmp, &mat); - } - - /* Add 1 pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel. */ - uint width = std::max(1U, (uint)gm.gmBlackBoxX + (this->fs == FS_NORMAL)); - uint height = std::max(1U, (uint)gm.gmBlackBoxY + (this->fs == FS_NORMAL)); - - /* Limit glyph size to prevent overflows later on. */ - if (width > 256 || height > 256) usererror("Font glyph is too large"); - - /* GDI 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.width = width; - sprite.height = height; - sprite.x_offs = gm.gmptGlyphOrigin.x; - sprite.y_offs = this->ascender - gm.gmptGlyphOrigin.y; - - if (size > 0) { - /* All pixel data returned by GDI is in the form of DWORD-aligned rows. - * For a non anti-aliased glyph, the returned bitmap has one bit per pixel. - * For anti-aliased rendering, GDI uses the strange value range of 0 to 64, - * inclusively. To map this to 0 to 255, we shift left by two and then - * subtract one. */ - uint pitch = Align(aa ? gm.gmBlackBoxX : std::max(gm.gmBlackBoxX / 8u, 1u), 4); - - /* Draw shadow for medium size. */ - if (this->fs == FS_NORMAL && !aa) { - for (uint y = 0; y < gm.gmBlackBoxY; y++) { - for (uint x = 0; x < gm.gmBlackBoxX; x++) { - if (aa ? (bmp[x + y * pitch] > 0) : HasBit(bmp[(x / 8) + y * pitch], 7 - (x % 8))) { - sprite.data[1 + x + (1 + y) * sprite.width].m = SHADOW_COLOUR; - sprite.data[1 + x + (1 + y) * sprite.width].a = aa ? (bmp[x + y * pitch] << 2) - 1 : 0xFF; - } - } - } - } - - for (uint y = 0; y < gm.gmBlackBoxY; y++) { - for (uint x = 0; x < gm.gmBlackBoxX; x++) { - if (aa ? (bmp[x + y * pitch] > 0) : HasBit(bmp[(x / 8) + y * pitch], 7 - (x % 8))) { - sprite.data[x + y * sprite.width].m = FACE_COLOUR; - sprite.data[x + y * sprite.width].a = aa ? (bmp[x + y * pitch] << 2) - 1 : 0xFF; - } - } - } - } - - GlyphEntry new_glyph; - new_glyph.sprite = BlitterFactory::GetCurrentBlitter()->Encode(&sprite, AllocateFont); - new_glyph.width = gm.gmCellIncX; - - this->SetGlyphPtr(key, &new_glyph); - - return new_glyph.sprite; -} - -/* virtual */ GlyphID Win32FontCache::MapCharToGlyph(WChar key) -{ - assert(IsPrintable(key)); - - if (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END) { - return this->parent->MapCharToGlyph(key); - } - - /* Convert characters outside of the BMP into surrogate pairs. */ - WCHAR chars[2]; - if (key >= 0x010000U) { - chars[0] = (WCHAR)(((key - 0x010000U) >> 10) + 0xD800); - chars[1] = (WCHAR)(((key - 0x010000U) & 0x3FF) + 0xDC00); - } else { - chars[0] = (WCHAR)(key & 0xFFFF); - } - - WORD glyphs[2] = {0, 0}; - GetGlyphIndicesW(this->dc, chars, key >= 0x010000U ? 2 : 1, glyphs, GGI_MARK_NONEXISTING_GLYPHS); - - return glyphs[0] != 0xFFFF ? glyphs[0] : 0; -} - -/* virtual */ const void *Win32FontCache::InternalGetFontTable(uint32 tag, size_t &length) -{ - DWORD len = GetFontData(this->dc, tag, 0, nullptr, 0); - - void *result = nullptr; - if (len != GDI_ERROR && len > 0) { - result = MallocT<BYTE>(len); - GetFontData(this->dc, tag, 0, result, len); - } - - length = len; - return result; -} - -/** - * Loads the GDI font. - * If a GDI font description is present, e.g. from the automatic font - * fallback search, use it. Otherwise, try to resolve it by font name. - * @param fs The font size to load. - */ -static void LoadWin32Font(FontSize fs) -{ - static const char *SIZE_TO_NAME[] = { "medium", "small", "large", "mono" }; - - FreeTypeSubSetting *settings = nullptr; - switch (fs) { - default: NOT_REACHED(); - case FS_SMALL: settings = &_freetype.small; break; - case FS_NORMAL: settings = &_freetype.medium; break; - case FS_LARGE: settings = &_freetype.large; break; - case FS_MONO: settings = &_freetype.mono; break; - } - - if (StrEmpty(settings->font)) return; - - LOGFONT logfont; - MemSetT(&logfont, 0); - logfont.lfPitchAndFamily = fs == FS_MONO ? FIXED_PITCH : VARIABLE_PITCH; - logfont.lfCharSet = DEFAULT_CHARSET; - logfont.lfOutPrecision = OUT_OUTLINE_PRECIS; - logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; - - if (settings->os_handle != nullptr) { - logfont = *(const LOGFONT *)settings->os_handle; - } else if (strchr(settings->font, '.') != nullptr) { - /* Might be a font file name, try load it. */ - - TCHAR fontPath[MAX_PATH] = {}; - - /* See if this is an absolute path. */ - if (FileExists(settings->font)) { - convert_to_fs(settings->font, fontPath, lengthof(fontPath), false); - } else { - /* Scan the search-paths to see if it can be found. */ - std::string full_font = FioFindFullPath(BASE_DIR, settings->font); - if (!full_font.empty()) { - convert_to_fs(full_font.c_str(), fontPath, lengthof(fontPath), false); - } - } - - if (fontPath[0] != 0) { - if (AddFontResourceEx(fontPath, FR_PRIVATE, 0) != 0) { - /* Try a nice little undocumented function first for getting the internal font name. - * Some documentation is found at: http://www.undocprint.org/winspool/getfontresourceinfo */ - typedef BOOL(WINAPI * PFNGETFONTRESOURCEINFO)(LPCTSTR, LPDWORD, LPVOID, DWORD); -#ifdef UNICODE - static PFNGETFONTRESOURCEINFO GetFontResourceInfo = (PFNGETFONTRESOURCEINFO)GetProcAddress(GetModuleHandle(_T("Gdi32")), "GetFontResourceInfoW"); -#else - static PFNGETFONTRESOURCEINFO GetFontResourceInfo = (PFNGETFONTRESOURCEINFO)GetProcAddress(GetModuleHandle(_T("Gdi32")), "GetFontResourceInfoA"); -#endif - - if (GetFontResourceInfo != nullptr) { - /* Try to query an array of LOGFONTs that describe the file. */ - DWORD len = 0; - if (GetFontResourceInfo(fontPath, &len, nullptr, 2) && len >= sizeof(LOGFONT)) { - LOGFONT *buf = (LOGFONT *)AllocaM(byte, len); - if (GetFontResourceInfo(fontPath, &len, buf, 2)) { - logfont = *buf; // Just use first entry. - } - } - } - - /* No dice yet. Use the file name as the font face name, hoping it matches. */ - if (logfont.lfFaceName[0] == 0) { - TCHAR fname[_MAX_FNAME]; - _tsplitpath(fontPath, nullptr, nullptr, fname, nullptr); - - _tcsncpy_s(logfont.lfFaceName, lengthof(logfont.lfFaceName), fname, _TRUNCATE); - logfont.lfWeight = strcasestr(settings->font, " bold") != nullptr || strcasestr(settings->font, "-bold") != nullptr ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts. - } - } else { - ShowInfoF("Unable to load file '%s' for %s font, using default windows font selection instead", settings->font, SIZE_TO_NAME[fs]); - } - } - } - - if (logfont.lfFaceName[0] == 0) { - logfont.lfWeight = strcasestr(settings->font, " bold") != nullptr ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts. - convert_to_fs(settings->font, logfont.lfFaceName, lengthof(logfont.lfFaceName), false); - } - - HFONT font = CreateFontIndirect(&logfont); - if (font == nullptr) { - ShowInfoF("Unable to use '%s' for %s font, Win32 reported error 0x%lX, using sprite font instead", settings->font, SIZE_TO_NAME[fs], GetLastError()); - return; - } - DeleteObject(font); - - new Win32FontCache(fs, logfont, settings->size); -} #endif /* WITH_FREETYPE */ @@ -1075,6 +701,7 @@ void InitFreeType(bool monospace) #ifdef WITH_FREETYPE LoadFreeTypeFont(fs); #elif defined(_WIN32) + extern void LoadWin32Font(FontSize fs); LoadWin32Font(fs); #endif } |