32 static const int _default_font_ascender[FS_END] = { 8, 5, 15, 8};
39 ascender(_default_font_ascender[fs]), descender(_default_font_ascender[fs] -
_default_font_height[fs]),
85 virtual const void *
GetFontTable(uint32 tag,
size_t &length) { length = 0;
return nullptr; }
127 default: NOT_REACHED();
129 case FS_NORMAL: base = SPR_ASCII_SPACE;
break;
130 case FS_SMALL: base = SPR_ASCII_SPACE_SMALL;
break;
131 case FS_LARGE: base = SPR_ASCII_SPACE_BIG;
break;
136 if (!SpriteExists(sprite))
continue;
141 for (uint i = 0; i <
lengthof(_default_unicode_map); i++) {
142 byte key = _default_unicode_map[i].key;
162 for (uint i = 0; i < 256; i++) {
178 return GetSprite(sprite,
ST_FONT);
200 #if defined(WITH_FREETYPE) || defined(_WIN32)
206 static const byte FACE_COLOUR = 1;
207 static const byte SHADOW_COLOUR = 2;
243 virtual const void *InternalGetFontTable(uint32 tag,
size_t &length) = 0;
244 virtual const Sprite *InternalGetGlyph(
GlyphID key,
bool aa) = 0;
254 virtual const void *
GetFontTable(uint32 tag,
size_t &length);
278 free(iter.second.second);
289 for (
int i = 0; i < 256; i++) {
292 for (
int j = 0; j < 256; j++) {
314 void TrueTypeFontCache::SetGlyphPtr(
GlyphID key,
const GlyphEntry *glyph,
bool duplicate)
317 DEBUG(freetype, 3,
"Allocating root glyph cache for size %u", this->
fs);
322 DEBUG(freetype, 3,
"Allocating glyph cache for range 0x%02X00, size %u",
GB(key, 8, 8), this->
fs);
326 DEBUG(freetype, 4,
"Set glyph for unicode character 0x%04X, size %u", key, this->
fs);
332 static void *AllocateFont(
size_t size)
334 return MallocT<byte>(size);
339 static bool GetFontAAState(
FontSize size)
345 default: NOT_REACHED();
363 if (glyph ==
nullptr || glyph->
sprite ==
nullptr) {
365 glyph = this->GetGlyphPtr(key);
373 if ((key & SPRITE_GLYPH) != 0)
return this->
parent->
GetGlyph(key);
377 if (glyph !=
nullptr && glyph->
sprite !=
nullptr)
return glyph->
sprite;
381 if (question_glyph == 0) {
384 #define CPSET { 0, 0, 0, 0, 1 }
385 #define CP___ { 0, 0, 0, 0, 0 }
387 CP___, CP___, CPSET, CPSET, CPSET, CPSET, CP___, CP___,
388 CP___, CPSET, CPSET, CP___, CP___, CPSET, CPSET, CP___,
389 CP___, CP___, CP___, CP___, CP___, CPSET, CPSET, CP___,
390 CP___, CP___, CP___, CP___, CPSET, CPSET, CP___, CP___,
391 CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___,
392 CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___,
393 CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___,
394 CP___, CP___, CP___, CP___, CP___, CP___, CP___, CP___,
395 CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___,
396 CP___, CP___, CP___, CPSET, CPSET, CP___, CP___, CP___,
406 builtin_questionmark_data
410 assert(spr !=
nullptr);
414 this->SetGlyphPtr(key, &new_glyph,
false);
419 glyph = this->GetGlyphPtr(question_glyph);
420 this->SetGlyphPtr(key, glyph,
true);
425 return this->InternalGetGlyph(key, GetFontAAState(this->
fs));
431 if (iter != this->
font_tables.data() + this->font_tables.size()) {
432 length = iter->second.first;
433 return iter->second.second;
436 const void *result = this->InternalGetFontTable(tag, length);
444 #include <ft2build.h>
445 #include FT_FREETYPE_H
447 #include FT_TRUETYPE_TABLES_H
455 virtual const void *InternalGetFontTable(uint32 tag,
size_t &length);
456 virtual const Sprite *InternalGetGlyph(
GlyphID key,
bool aa);
467 FT_Library _library =
nullptr;
478 assert(
face !=
nullptr);
480 this->SetFontSize(
fs,
face, pixels);
483 void FreeTypeFontCache::SetFontSize(
FontSize fs, FT_Face face,
int pixels)
488 pixels = scaled_height;
490 TT_Header *head = (TT_Header *)FT_Get_Sfnt_Table(this->face, ft_sfnt_head);
491 if (head !=
nullptr) {
495 pixels =
Clamp(std::min<uint>(head->Lowest_Rec_PPEM, 20u) + diff, scaled_height,
MAX_FONT_SIZE);
502 FT_Error err = FT_Set_Pixel_Sizes(this->face, 0, pixels);
503 if (err != FT_Err_Ok) {
506 FT_Bitmap_Size *bs = this->face->available_sizes;
507 int i = this->face->num_fixed_sizes;
512 if (
abs(pixels - bs->height) >=
abs(pixels - n))
continue;
514 chosen = this->face->num_fixed_sizes - i;
519 err = FT_Select_Size(this->face, chosen);
523 if (err == FT_Err_Ok) {
525 this->
ascender = this->face->size->metrics.ascender >> 6;
526 this->
descender = this->face->size->metrics.descender >> 6;
530 DEBUG(freetype, 0,
"Font size selection failed. Using FontCache defaults.");
545 default: NOT_REACHED();
554 if (_library ==
nullptr) {
555 if (FT_Init_FreeType(&_library) != FT_Err_Ok) {
556 ShowInfoF(
"Unable to initialize FreeType, using sprite fonts instead");
560 DEBUG(freetype, 2,
"Initialized");
563 FT_Face face =
nullptr;
566 FT_Error
error = FT_New_Face(_library,
settings->font, 0, &face);
568 #if defined(WITH_COCOA)
573 if (
error != FT_Err_Ok) {
576 if (!full_font.empty()) {
577 error = FT_New_Face(_library, full_font.c_str(), 0, &face);
578 #if defined(WITH_COCOA)
587 if (
error == FT_Err_Ok) {
588 DEBUG(freetype, 2,
"Requested '%s', using '%s %s'",
settings->font, face->family_name, face->style_name);
591 error = FT_Select_Charmap(face, ft_encoding_unicode);
592 if (
error == FT_Err_Ok)
goto found_face;
594 if (
error == FT_Err_Invalid_CharMap_Handle) {
598 FT_CharMap found = face->charmaps[0];
601 for (i = 0; i < face->num_charmaps; i++) {
602 FT_CharMap charmap = face->charmaps[i];
603 if (charmap->platform_id == 0 && charmap->encoding_id == 0) {
608 if (found !=
nullptr) {
609 error = FT_Set_Charmap(face, found);
610 if (
error == FT_Err_Ok)
goto found_face;
617 static const char *SIZE_TO_NAME[] = {
"medium",
"small",
"large",
"mono" };
618 ShowInfoF(
"Unable to use '%s' for %s font, FreeType reported error 0x%X, using sprite font instead",
settings->font, SIZE_TO_NAME[fs],
error);
631 FT_Done_Face(this->face);
632 this->face =
nullptr;
642 if (this->face !=
nullptr) this->SetFontSize(this->fs, this->face, this->
req_size);
648 const Sprite *FreeTypeFontCache::InternalGetGlyph(
GlyphID key,
bool aa)
650 FT_GlyphSlot slot = this->face->glyph;
652 FT_Load_Glyph(this->face, key, aa ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO);
653 FT_Render_Glyph(this->face->glyph, aa ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
656 aa = (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY);
659 uint
width = std::max(1U, (uint)slot->bitmap.width + (this->fs ==
FS_NORMAL));
660 uint
height = std::max(1U, (uint)slot->bitmap.rows + (this->fs ==
FS_NORMAL));
671 sprite.
x_offs = slot->bitmap_left;
676 for (uint y = 0; y < (uint)slot->bitmap.rows; y++) {
677 for (uint x = 0; x < (uint)slot->bitmap.width; x++) {
678 if (aa ? (slot->bitmap.buffer[x + y * slot->bitmap.pitch] > 0) :
HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
679 sprite.
data[1 + x + (1 + y) * sprite.
width].
m = SHADOW_COLOUR;
680 sprite.
data[1 + x + (1 + y) * sprite.
width].
a = aa ? slot->bitmap.buffer[x + y * slot->bitmap.pitch] : 0xFF;
686 for (uint y = 0; y < (uint)slot->bitmap.rows; y++) {
687 for (uint x = 0; x < (uint)slot->bitmap.width; x++) {
688 if (aa ? (slot->bitmap.buffer[x + y * slot->bitmap.pitch] > 0) :
HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
689 sprite.
data[x + y * sprite.
width].
m = FACE_COLOUR;
690 sprite.
data[x + y * sprite.
width].
a = aa ? slot->bitmap.buffer[x + y * slot->bitmap.pitch] : 0xFF;
695 GlyphEntry new_glyph;
697 new_glyph.
width = slot->advance.x >> 6;
699 this->SetGlyphPtr(key, &new_glyph);
701 return new_glyph.sprite;
707 assert(IsPrintable(key));
709 if (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END) {
713 return FT_Get_Char_Index(this->face, key);
716 const void *FreeTypeFontCache::InternalGetFontTable(uint32 tag,
size_t &length)
719 FT_Byte *result =
nullptr;
721 FT_Load_Sfnt_Table(this->face, tag, 0,
nullptr, &len);
724 result = MallocT<FT_Byte>(len);
725 FT_Load_Sfnt_Table(this->face, tag, 0, result, &len);
732 #elif defined(_WIN32)
735 #ifndef ANTIALIASED_QUALITY
736 #define ANTIALIASED_QUALITY 4
743 HFONT font =
nullptr;
748 void SetFontSize(
FontSize fs,
int pixels);
749 virtual const void *InternalGetFontTable(uint32 tag,
size_t &length);
750 virtual const Sprite *InternalGetGlyph(
GlyphID key,
bool aa);
753 Win32FontCache(
FontSize fs,
const LOGFONT &logfont,
int pixels);
757 virtual const char *
GetFontName() {
return WIDE_TO_MB(this->logfont.lfFaceName); }
759 virtual void *
GetOSHandle() {
return &this->logfont; }
769 Win32FontCache::Win32FontCache(
FontSize fs,
const LOGFONT &logfont,
int pixels) :
TrueTypeFontCache(fs, pixels), logfont(logfont)
771 this->dc = CreateCompatibleDC(
nullptr);
772 this->SetFontSize(
fs, pixels);
775 Win32FontCache::~Win32FontCache()
777 this->ClearFontCache();
779 DeleteObject(this->font);
782 void Win32FontCache::SetFontSize(
FontSize fs,
int pixels)
787 pixels = scaled_height;
789 HFONT temp = CreateFontIndirect(&this->logfont);
790 if (temp !=
nullptr) {
791 HGDIOBJ old = SelectObject(this->dc, temp);
793 UINT size = GetOutlineTextMetrics(this->dc, 0,
nullptr);
794 LPOUTLINETEXTMETRIC otm = (LPOUTLINETEXTMETRIC)
AllocaM(BYTE, size);
795 GetOutlineTextMetrics(this->dc, size, otm);
800 pixels =
Clamp(std::min(otm->otmusMinimumPPEM, 20u) + diff, scaled_height,
MAX_FONT_SIZE);
802 SelectObject(dc, old);
808 this->used_size = pixels;
811 this->logfont.lfHeight = -pixels;
812 this->logfont.lfWidth = 0;
813 this->logfont.lfOutPrecision = ANTIALIASED_QUALITY;
815 if (this->font !=
nullptr) {
816 SelectObject(dc, this->old_font);
817 DeleteObject(this->font);
819 this->font = CreateFontIndirect(&this->logfont);
820 this->old_font = SelectObject(this->dc, this->font);
823 UINT otmSize = GetOutlineTextMetrics(this->dc, 0,
nullptr);
824 POUTLINETEXTMETRIC otm = (POUTLINETEXTMETRIC)
AllocaM(BYTE, otmSize);
825 GetOutlineTextMetrics(this->dc, otmSize, otm);
827 this->units_per_em = otm->otmEMSquare;
828 this->ascender = otm->otmTextMetrics.tmAscent;
829 this->descender = otm->otmTextMetrics.tmDescent;
830 this->height = this->ascender + this->descender;
831 this->glyph_size.cx = otm->otmTextMetrics.tmMaxCharWidth;
832 this->glyph_size.cy = otm->otmTextMetrics.tmHeight;
834 DEBUG(freetype, 2,
"Loaded font '%s' with size %d",
FS2OTTD((LPTSTR)((BYTE *)otm + (ptrdiff_t)otm->otmpFullName)), pixels);
840 void Win32FontCache::ClearFontCache()
843 if (this->font !=
nullptr) this->SetFontSize(this->fs, this->req_size);
848 const Sprite *Win32FontCache::InternalGetGlyph(
GlyphID key,
bool aa)
851 MAT2 mat = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
854 DWORD size = this->glyph_size.cy *
Align(aa ? this->glyph_size.cx : std::max(this->glyph_size.cx / 8l, 1l), 4);
855 byte *bmp =
AllocaM(
byte, size);
856 size = GetGlyphOutline(this->dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, size, bmp, &mat);
858 if (size == GDI_ERROR) {
864 size = GetGlyphOutline(this->dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, 0,
nullptr, &mat);
865 if (size == GDI_ERROR)
usererror(
"Unable to render font glyph");
867 GetGlyphOutline(this->dc, key, GGO_GLYPH_INDEX | (aa ? GGO_GRAY8_BITMAP : GGO_BITMAP), &gm, size, bmp, &mat);
871 uint
width = std::max(1U, (uint)gm.gmBlackBoxX + (this->fs ==
FS_NORMAL));
872 uint
height = std::max(1U, (uint)gm.gmBlackBoxY + (this->fs ==
FS_NORMAL));
883 sprite.
x_offs = gm.gmptGlyphOrigin.x;
884 sprite.
y_offs = this->ascender - gm.gmptGlyphOrigin.y;
892 uint pitch =
Align(aa ? gm.gmBlackBoxX : std::max(gm.gmBlackBoxX / 8u, 1u), 4);
896 for (uint y = 0; y < gm.gmBlackBoxY; y++) {
897 for (uint x = 0; x < gm.gmBlackBoxX; x++) {
898 if (aa ? (bmp[x + y * pitch] > 0) :
HasBit(bmp[(x / 8) + y * pitch], 7 - (x % 8))) {
899 sprite.
data[1 + x + (1 + y) * sprite.
width].
m = SHADOW_COLOUR;
900 sprite.
data[1 + x + (1 + y) * sprite.
width].
a = aa ? (bmp[x + y * pitch] << 2) - 1 : 0xFF;
906 for (uint y = 0; y < gm.gmBlackBoxY; y++) {
907 for (uint x = 0; x < gm.gmBlackBoxX; x++) {
908 if (aa ? (bmp[x + y * pitch] > 0) :
HasBit(bmp[(x / 8) + y * pitch], 7 - (x % 8))) {
909 sprite.
data[x + y * sprite.
width].
m = FACE_COLOUR;
910 sprite.
data[x + y * sprite.
width].
a = aa ? (bmp[x + y * pitch] << 2) - 1 : 0xFF;
916 GlyphEntry new_glyph;
918 new_glyph.
width = gm.gmCellIncX;
920 this->SetGlyphPtr(key, &new_glyph);
922 return new_glyph.sprite;
927 assert(IsPrintable(key));
929 if (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END) {
930 return this->parent->MapCharToGlyph(key);
935 if (key >= 0x010000U) {
936 chars[0] = (WCHAR)(((key - 0x010000U) >> 10) + 0xD800);
937 chars[1] = (WCHAR)(((key - 0x010000U) & 0x3FF) + 0xDC00);
939 chars[0] = (WCHAR)(key & 0xFFFF);
942 WORD glyphs[2] = {0, 0};
943 GetGlyphIndicesW(this->dc, chars, key >= 0x010000U ? 2 : 1, glyphs, GGI_MARK_NONEXISTING_GLYPHS);
945 return glyphs[0] != 0xFFFF ? glyphs[0] : 0;
948 const void *Win32FontCache::InternalGetFontTable(uint32 tag,
size_t &length)
950 DWORD len = GetFontData(this->dc, tag, 0,
nullptr, 0);
952 void *result =
nullptr;
953 if (len != GDI_ERROR && len > 0) {
954 result = MallocT<BYTE>(len);
955 GetFontData(this->dc, tag, 0, result, len);
968 static void LoadWin32Font(
FontSize fs)
970 static const char *SIZE_TO_NAME[] = {
"medium",
"small",
"large",
"mono" };
974 default: NOT_REACHED();
985 logfont.lfPitchAndFamily = fs ==
FS_MONO ? FIXED_PITCH : VARIABLE_PITCH;
986 logfont.lfCharSet = DEFAULT_CHARSET;
987 logfont.lfOutPrecision = OUT_OUTLINE_PRECIS;
988 logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
990 if (
settings->os_handle !=
nullptr) {
991 logfont = *(
const LOGFONT *)
settings->os_handle;
992 }
else if (strchr(
settings->font,
'.') !=
nullptr) {
995 TCHAR fontPath[MAX_PATH] = {};
1003 if (!full_font.empty()) {
1008 if (fontPath[0] != 0) {
1009 if (AddFontResourceEx(fontPath, FR_PRIVATE, 0) != 0) {
1012 typedef BOOL(WINAPI * PFNGETFONTRESOURCEINFO)(LPCTSTR, LPDWORD, LPVOID, DWORD);
1014 static PFNGETFONTRESOURCEINFO GetFontResourceInfo = (PFNGETFONTRESOURCEINFO)GetProcAddress(GetModuleHandle(_T(
"Gdi32")),
"GetFontResourceInfoW");
1016 static PFNGETFONTRESOURCEINFO GetFontResourceInfo = (PFNGETFONTRESOURCEINFO)GetProcAddress(GetModuleHandle(_T(
"Gdi32")),
"GetFontResourceInfoA");
1019 if (GetFontResourceInfo !=
nullptr) {
1022 if (GetFontResourceInfo(fontPath, &len,
nullptr, 2) && len >=
sizeof(LOGFONT)) {
1023 LOGFONT *buf = (LOGFONT *)
AllocaM(
byte, len);
1024 if (GetFontResourceInfo(fontPath, &len, buf, 2)) {
1031 if (logfont.lfFaceName[0] == 0) {
1032 TCHAR fname[_MAX_FNAME];
1033 _tsplitpath(fontPath,
nullptr,
nullptr, fname,
nullptr);
1035 _tcsncpy_s(logfont.lfFaceName,
lengthof(logfont.lfFaceName), fname, _TRUNCATE);
1036 logfont.lfWeight = strcasestr(
settings->font,
" bold") !=
nullptr || strcasestr(
settings->font,
"-bold") !=
nullptr ? FW_BOLD : FW_NORMAL;
1039 ShowInfoF(
"Unable to load file '%s' for %s font, using default windows font selection instead",
settings->font, SIZE_TO_NAME[fs]);
1044 if (logfont.lfFaceName[0] == 0) {
1045 logfont.lfWeight = strcasestr(
settings->font,
" bold") !=
nullptr ? FW_BOLD : FW_NORMAL;
1049 HFONT font = CreateFontIndirect(&logfont);
1050 if (font ==
nullptr) {
1051 ShowInfoF(
"Unable to use '%s' for %s font, Win32 reported error 0x%lX, using sprite font instead",
settings->font, SIZE_TO_NAME[fs], GetLastError());
1056 new Win32FontCache(fs, logfont,
settings->size);
1070 if (monospace != (fs ==
FS_MONO))
continue;
1075 #ifdef WITH_FREETYPE
1077 #elif defined(_WIN32)
1093 #ifdef WITH_FREETYPE
1094 FT_Done_FreeType(_library);