1 /* FreeType 2 and UTF-8 encoding support for
8 #include "ft2_fontdefs.h"
11 static int img_fontmap[256] = {
12 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
13 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
14 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // shift+digit line
15 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // digits
16 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // caps
17 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // caps
18 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small
19 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small
20 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // specials
21 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // faces
22 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
23 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
24 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
25 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
26 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
27 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
31 * Some big blocks of Unicode characters, according to
32 * http://www.unicode.org/Public/UNIDATA/Blocks.txt
34 * Let's call these "bigblocks".
35 * These appears in a text in a "spreaded" way, ordinary maps will
36 * waste huge amount of resources rendering/caching unused glyphs.
38 * So, another matter is invented to counter this: incremental maps,
39 * in which only used glyphs may present.
41 static const Uchar unicode_bigblocks[] = {
42 0x3400, 0x4DBF, // 6592 CJK Unified Ideographs Extension A
43 0x4E00, 0x9FFF, // 20992 CJK Unified Ideographs
44 0xAC00, 0xD7AF, // 11184 Hangul Syllables
45 0xE000, 0xF8FF, // 6400 Private Use Area
46 0x10000, 0x10FFFF // Everything above
50 ================================================================================
51 CVars introduced with the freetype extension
52 ================================================================================
55 cvar_t r_font_disable_freetype = {CF_CLIENT | CF_ARCHIVE, "r_font_disable_freetype", "0", "disable freetype support for fonts entirely"};
56 cvar_t r_font_use_alpha_textures = {CF_CLIENT | CF_ARCHIVE, "r_font_use_alpha_textures", "0", "[deprecated, not effective] use alpha-textures for font rendering, this should safe memory"};
57 cvar_t r_font_size_snapping = {CF_CLIENT | CF_ARCHIVE, "r_font_size_snapping", "1", "stick to good looking font sizes whenever possible - bad when the mod doesn't support it!"};
58 cvar_t r_font_kerning = {CF_CLIENT | CF_ARCHIVE, "r_font_kerning", "1", "Use kerning if available"};
59 cvar_t r_font_diskcache = {CF_CLIENT | CF_ARCHIVE, "r_font_diskcache", "0", "[deprecated, not effective] save font textures to disk for future loading rather than generating them every time"};
60 cvar_t r_font_compress = {CF_CLIENT | CF_ARCHIVE, "r_font_compress", "0", "use texture compression on font textures to save video memory"};
61 cvar_t r_font_nonpoweroftwo = {CF_CLIENT | CF_ARCHIVE, "r_font_nonpoweroftwo", "1", "use nonpoweroftwo textures for font (saves memory, potentially slower)"};
62 cvar_t developer_font = {CF_CLIENT | CF_ARCHIVE, "developer_font", "0", "prints debug messages about fonts"};
64 cvar_t r_font_disable_incmaps = {CF_CLIENT | CF_ARCHIVE, "r_font_disable_incmaps", "0", "always to load a full glyph map for individual unmapped character, even when it will mean extreme resources waste"};
66 #ifndef DP_FREETYPE_STATIC
69 ================================================================================
70 Function definitions. Taken from the freetype2 headers.
71 ================================================================================
76 (*qFT_Init_FreeType)( FT_Library *alibrary );
78 (*qFT_Done_FreeType)( FT_Library library );
81 (*qFT_New_Face)( FT_Library library,
82 const char* filepathname,
87 (*qFT_New_Memory_Face)( FT_Library library,
88 const FT_Byte* file_base,
93 (*qFT_Done_Face)( FT_Face face );
95 (*qFT_Select_Size)( FT_Face face,
96 FT_Int strike_index );
98 (*qFT_Request_Size)( FT_Face face,
99 FT_Size_Request req );
100 FT_EXPORT( FT_Error )
101 (*qFT_Set_Char_Size)( FT_Face face,
102 FT_F26Dot6 char_width,
103 FT_F26Dot6 char_height,
104 FT_UInt horz_resolution,
105 FT_UInt vert_resolution );
106 FT_EXPORT( FT_Error )
107 (*qFT_Set_Pixel_Sizes)( FT_Face face,
109 FT_UInt pixel_height );
110 FT_EXPORT( FT_Error )
111 (*qFT_Load_Glyph)( FT_Face face,
113 FT_Int32 load_flags );
114 FT_EXPORT( FT_Error )
115 (*qFT_Load_Char)( FT_Face face,
117 FT_Int32 load_flags );
119 (*qFT_Get_Char_Index)( FT_Face face,
121 FT_EXPORT( FT_Error )
122 (*qFT_Render_Glyph)( FT_GlyphSlot slot,
123 FT_Render_Mode render_mode );
124 FT_EXPORT( FT_Error )
125 (*qFT_Get_Kerning)( FT_Face face,
129 FT_Vector *akerning );
130 FT_EXPORT( FT_Error )
131 (*qFT_Attach_Stream)( FT_Face face,
132 FT_Open_Args* parameters );
134 ================================================================================
135 Support for dynamically loading the FreeType2 library
136 ================================================================================
139 static dllfunction_t ft2funcs[] =
141 {"FT_Init_FreeType", (void **) &qFT_Init_FreeType},
142 {"FT_Done_FreeType", (void **) &qFT_Done_FreeType},
143 //{"FT_New_Face", (void **) &qFT_New_Face},
144 {"FT_New_Memory_Face", (void **) &qFT_New_Memory_Face},
145 {"FT_Done_Face", (void **) &qFT_Done_Face},
146 {"FT_Select_Size", (void **) &qFT_Select_Size},
147 {"FT_Request_Size", (void **) &qFT_Request_Size},
148 {"FT_Set_Char_Size", (void **) &qFT_Set_Char_Size},
149 {"FT_Set_Pixel_Sizes", (void **) &qFT_Set_Pixel_Sizes},
150 {"FT_Load_Glyph", (void **) &qFT_Load_Glyph},
151 {"FT_Load_Char", (void **) &qFT_Load_Char},
152 {"FT_Get_Char_Index", (void **) &qFT_Get_Char_Index},
153 {"FT_Render_Glyph", (void **) &qFT_Render_Glyph},
154 {"FT_Get_Kerning", (void **) &qFT_Get_Kerning},
155 {"FT_Attach_Stream", (void **) &qFT_Attach_Stream},
159 /// Handle for FreeType2 DLL
160 static dllhandle_t ft2_dll = NULL;
164 FT_EXPORT( FT_Error )
165 (FT_Init_FreeType)( FT_Library *alibrary );
166 FT_EXPORT( FT_Error )
167 (FT_Done_FreeType)( FT_Library library );
169 FT_EXPORT( FT_Error )
170 (FT_New_Face)( FT_Library library,
171 const char* filepathname,
175 FT_EXPORT( FT_Error )
176 (FT_New_Memory_Face)( FT_Library library,
177 const FT_Byte* file_base,
181 FT_EXPORT( FT_Error )
182 (FT_Done_Face)( FT_Face face );
183 FT_EXPORT( FT_Error )
184 (FT_Select_Size)( FT_Face face,
185 FT_Int strike_index );
186 FT_EXPORT( FT_Error )
187 (FT_Request_Size)( FT_Face face,
188 FT_Size_Request req );
189 FT_EXPORT( FT_Error )
190 (FT_Set_Char_Size)( FT_Face face,
191 FT_F26Dot6 char_width,
192 FT_F26Dot6 char_height,
193 FT_UInt horz_resolution,
194 FT_UInt vert_resolution );
195 FT_EXPORT( FT_Error )
196 (FT_Set_Pixel_Sizes)( FT_Face face,
198 FT_UInt pixel_height );
199 FT_EXPORT( FT_Error )
200 (FT_Load_Glyph)( FT_Face face,
202 FT_Int32 load_flags );
203 FT_EXPORT( FT_Error )
204 (FT_Load_Char)( FT_Face face,
206 FT_Int32 load_flags );
208 (FT_Get_Char_Index)( FT_Face face,
210 FT_EXPORT( FT_Error )
211 (FT_Render_Glyph)( FT_GlyphSlot slot,
212 FT_Render_Mode render_mode );
213 FT_EXPORT( FT_Error )
214 (FT_Get_Kerning)( FT_Face face,
218 FT_Vector *akerning );
219 FT_EXPORT( FT_Error )
220 (FT_Attach_Stream)( FT_Face face,
221 FT_Open_Args* parameters );
223 #define qFT_Init_FreeType FT_Init_FreeType
224 #define qFT_Done_FreeType FT_Done_FreeType
225 //#define qFT_New_Face FT_New_Face
226 #define qFT_New_Memory_Face FT_New_Memory_Face
227 #define qFT_Done_Face FT_Done_Face
228 #define qFT_Select_Size FT_Select_Size
229 #define qFT_Request_Size FT_Request_Size
230 #define qFT_Set_Char_Size FT_Set_Char_Size
231 #define qFT_Set_Pixel_Sizes FT_Set_Pixel_Sizes
232 #define qFT_Load_Glyph FT_Load_Glyph
233 #define qFT_Load_Char FT_Load_Char
234 #define qFT_Get_Char_Index FT_Get_Char_Index
235 #define qFT_Render_Glyph FT_Render_Glyph
236 #define qFT_Get_Kerning FT_Get_Kerning
237 #define qFT_Attach_Stream FT_Attach_Stream
241 /// Memory pool for fonts
242 static mempool_t *font_mempool= NULL;
244 /// FreeType library handle
245 static FT_Library font_ft2lib = NULL;
247 #define POSTPROCESS_MAXRADIUS 8
250 unsigned char *buf, *buf2;
251 int bufsize, bufwidth, bufheight, bufpitch;
252 float blur, outline, shadowx, shadowy, shadowz;
253 int padding_t, padding_b, padding_l, padding_r, blurpadding_lt, blurpadding_rb, outlinepadding_t, outlinepadding_b, outlinepadding_l, outlinepadding_r;
254 unsigned char circlematrix[2*POSTPROCESS_MAXRADIUS+1][2*POSTPROCESS_MAXRADIUS+1];
255 unsigned char gausstable[2*POSTPROCESS_MAXRADIUS+1];
258 static font_postprocess_t pp;
260 typedef struct fontfilecache_s
265 char path[MAX_QPATH];
268 #define MAX_FONTFILES 8
269 static fontfilecache_t fontfiles[MAX_FONTFILES];
270 static const unsigned char *fontfilecache_LoadFile(const char *path, qbool quiet, fs_offset_t *filesizepointer)
275 for(i = 0; i < MAX_FONTFILES; ++i)
277 if(fontfiles[i].refcount > 0)
278 if(!strcmp(path, fontfiles[i].path))
280 *filesizepointer = fontfiles[i].len;
281 ++fontfiles[i].refcount;
282 return fontfiles[i].buf;
286 buf = FS_LoadFile(path, font_mempool, quiet, filesizepointer);
289 for(i = 0; i < MAX_FONTFILES; ++i)
290 if(fontfiles[i].refcount <= 0)
292 strlcpy(fontfiles[i].path, path, sizeof(fontfiles[i].path));
293 fontfiles[i].len = *filesizepointer;
294 fontfiles[i].buf = buf;
295 fontfiles[i].refcount = 1;
302 static void fontfilecache_Free(const unsigned char *buf)
305 for(i = 0; i < MAX_FONTFILES; ++i)
307 if(fontfiles[i].refcount > 0)
308 if(fontfiles[i].buf == buf)
310 if(--fontfiles[i].refcount <= 0)
312 Mem_Free(fontfiles[i].buf);
313 fontfiles[i].buf = NULL;
318 // if we get here, it used regular allocation
319 Mem_Free((void *) buf);
321 static void fontfilecache_FreeAll(void)
324 for(i = 0; i < MAX_FONTFILES; ++i)
326 if(fontfiles[i].refcount > 0)
327 Mem_Free(fontfiles[i].buf);
328 fontfiles[i].buf = NULL;
329 fontfiles[i].refcount = 0;
337 Unload the FreeType2 DLL
340 void Font_CloseLibrary (void)
342 fontfilecache_FreeAll();
344 Mem_FreePool(&font_mempool);
345 if (font_ft2lib && qFT_Done_FreeType)
347 qFT_Done_FreeType(font_ft2lib);
350 #ifndef DP_FREETYPE_STATIC
351 Sys_FreeLibrary (&ft2_dll);
360 Try to load the FreeType2 DLL
363 qbool Font_OpenLibrary (void)
365 #ifndef DP_FREETYPE_STATIC
366 const char* dllnames [] =
371 #elif defined(MACOSX)
372 "libfreetype.6.dylib",
382 if (r_font_disable_freetype.integer)
385 #ifndef DP_FREETYPE_STATIC
391 if (!Sys_LoadDependency (dllnames, &ft2_dll, ft2funcs))
401 Initialize the freetype2 font subsystem
405 void font_start(void)
407 if (!Font_OpenLibrary())
410 if (qFT_Init_FreeType(&font_ft2lib))
412 Con_Print(CON_ERROR "ERROR: Failed to initialize the FreeType2 library!\n");
417 font_mempool = Mem_AllocPool("FONT", 0, NULL);
420 Con_Print(CON_ERROR "ERROR: Failed to allocate FONT memory pool!\n");
426 void font_shutdown(void)
429 for (i = 0; i < dp_fonts.maxsize; ++i)
431 if (dp_fonts.f[i].ft2)
433 Font_UnloadFont(dp_fonts.f[i].ft2);
434 dp_fonts.f[i].ft2 = NULL;
440 void font_newmap(void)
446 Cvar_RegisterVariable(&r_font_nonpoweroftwo);
447 Cvar_RegisterVariable(&r_font_disable_freetype);
448 Cvar_RegisterVariable(&r_font_use_alpha_textures);
449 Cvar_RegisterVariable(&r_font_size_snapping);
450 Cvar_RegisterVariable(&r_font_kerning);
451 Cvar_RegisterVariable(&r_font_diskcache);
452 Cvar_RegisterVariable(&r_font_compress);
453 Cvar_RegisterVariable(&developer_font);
455 Cvar_RegisterVariable(&r_font_disable_incmaps);
457 // let's open it at startup already
462 ================================================================================
463 Implementation of a more or less lazy font loading and rendering code.
464 ================================================================================
467 #include "ft2_fontdefs.h"
469 ft2_font_t *Font_Alloc(void)
471 #ifndef DP_FREETYPE_STATIC
474 if (r_font_disable_freetype.integer)
477 return (ft2_font_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_t));
480 static qbool Font_Attach(ft2_font_t *font, ft2_attachment_t *attachment)
482 ft2_attachment_t *na;
484 font->attachmentcount++;
485 na = (ft2_attachment_t*)Mem_Alloc(font_mempool, sizeof(font->attachments[0]) * font->attachmentcount);
488 if (font->attachments && font->attachmentcount > 1)
490 memcpy(na, font->attachments, sizeof(font->attachments[0]) * (font->attachmentcount - 1));
491 Mem_Free(font->attachments);
493 memcpy(na + sizeof(font->attachments[0]) * (font->attachmentcount - 1), attachment, sizeof(*attachment));
494 font->attachments = na;
498 float Font_VirtualToRealSize(float sz)
506 //vw = ((vid.width > 0) ? vid.width : vid_width.value);
507 vh = ((vid.height > 0) ? vid.height : vid_height.value);
508 // now try to scale to our actual size:
509 sn = sz * vh / vid_conheight.value;
511 if ( sn - (float)si >= 0.5 )
516 float Font_SnapTo(float val, float snapwidth)
518 return floor(val / snapwidth + 0.5f) * snapwidth;
521 static qbool Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font);
522 static qbool Font_LoadSize(ft2_font_t *font, float size, qbool check_only);
523 qbool Font_LoadFont(const char *name, dp_font_t *dpfnt)
526 ft2_font_t *ft2, *fbfont, *fb;
536 // check if a fallback font has been specified, if it has been, and the
537 // font fails to load, use the image font as main font
538 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
540 if (dpfnt->fallbacks[i][0])
544 if (!Font_LoadFile(name, dpfnt->req_face, &dpfnt->settings, ft2))
546 if (i >= MAX_FONT_FALLBACKS)
552 strlcpy(ft2->name, name, sizeof(ft2->name));
553 ft2->image_font = true;
554 ft2->has_kerning = false;
558 ft2->image_font = false;
561 // attempt to load fallback fonts:
563 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
565 if (!dpfnt->fallbacks[i][0])
567 if (! (fb = Font_Alloc()) )
569 Con_Printf(CON_ERROR "Failed to allocate font for fallback %i of font %s\n", i, name);
573 if (!Font_LoadFile(dpfnt->fallbacks[i], dpfnt->fallback_faces[i], &dpfnt->settings, fb))
575 if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.tga", dpfnt->fallbacks[i])))
576 if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.png", dpfnt->fallbacks[i])))
577 if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.jpg", dpfnt->fallbacks[i])))
578 if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.pcx", dpfnt->fallbacks[i])))
579 Con_Printf(CON_ERROR "Failed to load font %s for fallback %i of font %s\n", dpfnt->fallbacks[i], i, name);
584 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
586 if (Font_LoadSize(fb, Font_VirtualToRealSize(dpfnt->req_sizes[s]), true))
591 Con_Printf(CON_ERROR "Failed to allocate font for fallback %i of font %s\n", i, name);
596 // at least one size of the fallback font loaded successfully
602 if (fbfont == ft2 && ft2->image_font)
604 // no fallbacks were loaded successfully:
611 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
613 if (Font_LoadSize(ft2, Font_VirtualToRealSize(dpfnt->req_sizes[s]), false))
618 // loading failed for every requested size
619 Font_UnloadFont(ft2);
625 //Con_Printf("%i sizes loaded\n", count);
630 static qbool Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font)
633 char filename[MAX_QPATH];
636 const unsigned char *data;
637 fs_offset_t datasize;
639 memset(font, 0, sizeof(*font));
641 if (!Font_OpenLibrary())
643 if (!r_font_disable_freetype.integer)
645 Con_Printf(CON_WARN "WARNING: can't open load font %s\n"
646 "You need the FreeType2 DLL to load font files\n",
652 font->settings = settings;
654 namelen = strlen(name);
655 if (namelen + 5 > sizeof(filename))
657 Con_Printf(CON_WARN "WARNING: too long font name. Cannot load this.\n");
661 // try load direct file
662 memcpy(filename, name, namelen+1);
663 data = fontfilecache_LoadFile(filename, false, &datasize);
667 memcpy(filename + namelen, ".ttf", 5);
668 data = fontfilecache_LoadFile(filename, false, &datasize);
673 memcpy(filename + namelen, ".otf", 5);
674 data = fontfilecache_LoadFile(filename, false, &datasize);
679 ft2_attachment_t afm;
681 memcpy(filename + namelen, ".pfb", 5);
682 data = fontfilecache_LoadFile(filename, false, &datasize);
686 memcpy(filename + namelen, ".afm", 5);
687 afm.data = fontfilecache_LoadFile(filename, false, &afm.size);
690 Font_Attach(font, &afm);
695 // FS_LoadFile being not-quiet should print an error :)
698 Con_DPrintf("Loading font %s face %i...\n", filename, _face);
700 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
701 if (status && _face != 0)
703 Con_Printf(CON_ERROR "Failed to load face %i of %s. Falling back to face 0\n", _face, name);
705 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
710 Con_Printf(CON_ERROR "ERROR: can't create face for %s\n"
711 "Error %i\n", // TODO: error strings
713 Font_UnloadFont(font);
717 // add the attachments
718 for (i = 0; i < font->attachmentcount; ++i)
721 memset(&args, 0, sizeof(args));
722 args.flags = FT_OPEN_MEMORY;
723 args.memory_base = (const FT_Byte*)font->attachments[i].data;
724 args.memory_size = font->attachments[i].size;
725 if (qFT_Attach_Stream((FT_Face)font->face, &args))
726 Con_Printf(CON_ERROR "Failed to add attachment %u to %s\n", (unsigned)i, font->name);
729 strlcpy(font->name, name, sizeof(font->name));
730 font->image_font = false;
731 font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
735 static void Font_Postprocess_Update(ft2_font_t *fnt, int bpp, int w, int h)
738 float gausstable[2*POSTPROCESS_MAXRADIUS+1];
739 qbool need_gauss = (!pp.buf || pp.blur != fnt->settings->blur || pp.shadowz != fnt->settings->shadowz);
740 qbool need_circle = (!pp.buf || pp.outline != fnt->settings->outline || pp.shadowx != fnt->settings->shadowx || pp.shadowy != fnt->settings->shadowy);
741 pp.blur = fnt->settings->blur;
742 pp.outline = fnt->settings->outline;
743 pp.shadowx = fnt->settings->shadowx;
744 pp.shadowy = fnt->settings->shadowy;
745 pp.shadowz = fnt->settings->shadowz;
746 pp.outlinepadding_l = bound(0, ceil(pp.outline - pp.shadowx), POSTPROCESS_MAXRADIUS);
747 pp.outlinepadding_r = bound(0, ceil(pp.outline + pp.shadowx), POSTPROCESS_MAXRADIUS);
748 pp.outlinepadding_t = bound(0, ceil(pp.outline - pp.shadowy), POSTPROCESS_MAXRADIUS);
749 pp.outlinepadding_b = bound(0, ceil(pp.outline + pp.shadowy), POSTPROCESS_MAXRADIUS);
750 pp.blurpadding_lt = bound(0, ceil(pp.blur - pp.shadowz), POSTPROCESS_MAXRADIUS);
751 pp.blurpadding_rb = bound(0, ceil(pp.blur + pp.shadowz), POSTPROCESS_MAXRADIUS);
752 pp.padding_l = pp.blurpadding_lt + pp.outlinepadding_l;
753 pp.padding_r = pp.blurpadding_rb + pp.outlinepadding_r;
754 pp.padding_t = pp.blurpadding_lt + pp.outlinepadding_t;
755 pp.padding_b = pp.blurpadding_rb + pp.outlinepadding_b;
759 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
760 gausstable[POSTPROCESS_MAXRADIUS+x] = (pp.blur > 0 ? exp(-(pow(x + pp.shadowz, 2))/(pp.blur*pp.blur * 2)) : (floor(x + pp.shadowz + 0.5) == 0));
761 for(x = -pp.blurpadding_rb; x <= pp.blurpadding_lt; ++x)
762 sum += gausstable[POSTPROCESS_MAXRADIUS+x];
763 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
764 pp.gausstable[POSTPROCESS_MAXRADIUS+x] = floor(gausstable[POSTPROCESS_MAXRADIUS+x] / sum * 255 + 0.5);
768 for(y = -POSTPROCESS_MAXRADIUS; y <= POSTPROCESS_MAXRADIUS; ++y)
769 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
771 float d = pp.outline + 1 - sqrt(pow(x + pp.shadowx, 2) + pow(y + pp.shadowy, 2));
772 pp.circlematrix[POSTPROCESS_MAXRADIUS+y][POSTPROCESS_MAXRADIUS+x] = (d >= 1) ? 255 : (d <= 0) ? 0 : floor(d * 255 + 0.5);
775 pp.bufwidth = w + pp.padding_l + pp.padding_r;
776 pp.bufheight = h + pp.padding_t + pp.padding_b;
777 pp.bufpitch = pp.bufwidth;
778 needed = pp.bufwidth * pp.bufheight;
779 if(!pp.buf || pp.bufsize < needed * 2)
783 pp.bufsize = needed * 4;
784 pp.buf = (unsigned char *)Mem_Alloc(font_mempool, pp.bufsize);
785 pp.buf2 = pp.buf + needed;
789 static void Font_Postprocess(ft2_font_t *fnt, unsigned char *imagedata, int pitch, int bpp, int w, int h, int *pad_l, int *pad_r, int *pad_t, int *pad_b)
793 // calculate gauss table
794 Font_Postprocess_Update(fnt, bpp, w, h);
799 // perform operation, not exceeding the passed padding values,
800 // but possibly reducing them
801 *pad_l = min(*pad_l, pp.padding_l);
802 *pad_r = min(*pad_r, pp.padding_r);
803 *pad_t = min(*pad_t, pp.padding_t);
804 *pad_b = min(*pad_b, pp.padding_b);
806 // outline the font (RGBA only)
807 if(bpp == 4 && (pp.outline > 0 || pp.blur > 0 || pp.shadowx != 0 || pp.shadowy != 0 || pp.shadowz != 0)) // we can only do this in BGRA
809 // this is like mplayer subtitle rendering
810 // bbuffer, bitmap buffer: this is our font
811 // abuffer, alpha buffer: this is pp.buf
812 // tmp: this is pp.buf2
814 // create outline buffer
815 memset(pp.buf, 0, pp.bufwidth * pp.bufheight);
816 for(y = -*pad_t; y < h + *pad_b; ++y)
817 for(x = -*pad_l; x < w + *pad_r; ++x)
819 int x1 = max(-x, -pp.outlinepadding_r);
820 int y1 = max(-y, -pp.outlinepadding_b);
821 int x2 = min(pp.outlinepadding_l, w-1-x);
822 int y2 = min(pp.outlinepadding_t, h-1-y);
826 for(my = y1; my <= y2; ++my)
827 for(mx = x1; mx <= x2; ++mx)
829 cur = pp.circlematrix[POSTPROCESS_MAXRADIUS+my][POSTPROCESS_MAXRADIUS+mx] * (int)imagedata[(x+mx) * bpp + pitch * (y+my) + (bpp - 1)];
833 pp.buf[((x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t))] = (highest + 128) / 255;
836 // blur the outline buffer
837 if(pp.blur > 0 || pp.shadowz != 0)
840 for(y = 0; y < pp.bufheight; ++y)
841 for(x = 0; x < pp.bufwidth; ++x)
843 int x1 = max(-x, -pp.blurpadding_rb);
844 int x2 = min(pp.blurpadding_lt, pp.bufwidth-1-x);
847 for(mx = x1; mx <= x2; ++mx)
848 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+mx] * (int)pp.buf[(x+mx) + pp.bufpitch * y];
849 pp.buf2[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
853 for(y = 0; y < pp.bufheight; ++y)
854 for(x = 0; x < pp.bufwidth; ++x)
856 int y1 = max(-y, -pp.blurpadding_rb);
857 int y2 = min(pp.blurpadding_lt, pp.bufheight-1-y);
860 for(my = y1; my <= y2; ++my)
861 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+my] * (int)pp.buf2[x + pp.bufpitch * (y+my)];
862 pp.buf[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
866 // paste the outline below the font
867 for(y = -*pad_t; y < h + *pad_b; ++y)
868 for(x = -*pad_l; x < w + *pad_r; ++x)
870 unsigned char outlinealpha = pp.buf[(x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t)];
873 unsigned char oldalpha = imagedata[x * bpp + pitch * y + (bpp - 1)];
874 // a' = 1 - (1 - a1) (1 - a2)
875 unsigned char newalpha = 255 - ((255 - (int)outlinealpha) * (255 - (int)oldalpha)) / 255; // this is >= oldalpha
876 // c' = (a2 c2 - a1 a2 c1 + a1 c1) / a' = (a2 c2 + a1 (1 - a2) c1) / a'
877 unsigned char oldfactor = (255 * (int)oldalpha) / newalpha;
878 //unsigned char outlinefactor = ((255 - oldalpha) * (int)outlinealpha) / newalpha;
880 for(i = 0; i < bpp-1; ++i)
882 unsigned char c = imagedata[x * bpp + pitch * y + i];
883 c = (c * (int)oldfactor) / 255 /* + outlinecolor[i] * (int)outlinefactor */;
884 imagedata[x * bpp + pitch * y + i] = c;
886 imagedata[x * bpp + pitch * y + (bpp - 1)] = newalpha;
888 //imagedata[x * bpp + pitch * y + (bpp - 1)] |= 0x80;
894 // perform operation, not exceeding the passed padding values,
895 // but possibly reducing them
896 *pad_l = min(*pad_l, pp.padding_l);
897 *pad_r = min(*pad_r, pp.padding_r);
898 *pad_t = min(*pad_t, pp.padding_t);
899 *pad_b = min(*pad_b, pp.padding_b);
903 // just calculate parameters
904 *pad_l = pp.padding_l;
905 *pad_r = pp.padding_r;
906 *pad_t = pp.padding_t;
907 *pad_b = pp.padding_b;
911 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size);
912 static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap, int *outmapch, qbool incmap_ok);
913 static qbool Font_LoadSize(ft2_font_t *font, float size, qbool check_only)
916 ft2_font_map_t *fmap, temp;
917 int gpad_l, gpad_r, gpad_t, gpad_b;
919 if (!(size > 0.001f && size < 1000.0f))
924 if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
927 for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
929 if (!font->font_maps[map_index])
931 // if a similar size has already been loaded, ignore this one
932 //abs(font->font_maps[map_index]->size - size) < 4
933 if (font->font_maps[map_index]->size == size)
937 if (map_index >= MAX_FONT_SIZES)
942 if (font->image_font)
943 fontface = (FT_Face)font->next->face;
945 fontface = (FT_Face)font->face;
946 return (Font_SearchSize(font, fontface, size) > 0);
949 Font_Postprocess(font, NULL, 0, 4, size*2, size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
951 memset(&temp, 0, sizeof(temp));
953 temp.glyphSize = size*2 + max(gpad_l + gpad_r, gpad_t + gpad_b);
954 if (!r_font_nonpoweroftwo.integer)
955 temp.glyphSize = CeilPowerOf2(temp.glyphSize);
956 temp.sfx = (1.0/64.0)/(double)size;
957 temp.sfy = (1.0/64.0)/(double)size;
958 temp.intSize = -1; // negative value: LoadMap must search now :)
959 if (!Font_LoadMap(font, &temp, 0, &fmap, NULL, false))
961 Con_Printf(CON_ERROR "ERROR: can't load the first character map for %s\n"
964 Font_UnloadFont(font);
967 font->font_maps[map_index] = temp.next;
969 fmap->sfx = temp.sfx;
970 fmap->sfy = temp.sfy;
972 // load the default kerning vector:
973 if (font->has_kerning)
977 fmap->kerning = (ft2_kerning_t *)Mem_Alloc(font_mempool, sizeof(ft2_kerning_t));
978 for (l = 0; l < 256; ++l)
980 for (r = 0; r < 256; ++r)
983 ul = qFT_Get_Char_Index((FT_Face)font->face, l);
984 ur = qFT_Get_Char_Index((FT_Face)font->face, r);
985 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
987 fmap->kerning->kerning[l][r][0] = 0;
988 fmap->kerning->kerning[l][r][1] = 0;
992 fmap->kerning->kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
993 fmap->kerning->kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
1001 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
1004 float value = 1000000;
1006 int matchsize = -10000;
1008 float fsize_x, fsize_y;
1009 ft2_font_map_t **maps = font->font_maps;
1011 fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value;
1013 fsize_x = *outw * vid.width / vid_conwidth.value;
1015 fsize_y = *outh * vid.height / vid_conheight.value;
1020 fsize_x = fsize_y = 16;
1030 for (m = 0; m < MAX_FONT_SIZES; ++m)
1034 // "round up" to the bigger size if two equally-valued matches exist
1035 nval = 0.5 * (fabs(maps[m]->size - fsize_x) + fabs(maps[m]->size - fsize_y));
1036 if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
1040 matchsize = maps[m]->size;
1041 if (value == 0) // there is no better match
1045 if (value <= r_font_size_snapping.value)
1047 // do NOT keep the aspect for perfect rendering
1048 if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
1049 if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width;
1054 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
1056 if (index < 0 || index >= MAX_FONT_SIZES)
1058 return font->font_maps[index];
1061 static qbool Font_SetSize(ft2_font_t *font, float w, float h)
1063 if (font->currenth == h &&
1064 ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
1065 font->currentw == w)) // same size has been requested
1069 // sorry, but freetype doesn't seem to care about other sizes
1072 if (font->image_font)
1074 if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
1079 if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
1087 qbool Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
1089 ft2_font_map_t *fmap;
1090 if (!font->has_kerning || !r_font_kerning.integer)
1092 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1094 fmap = font->font_maps[map_index];
1097 if (left < 256 && right < 256)
1099 //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
1100 // quick-kerning, be aware of the size: scale it
1101 if (outx) *outx = fmap->kerning->kerning[left][right][0];// * (w / (float)fmap->size);
1102 if (outy) *outy = fmap->kerning->kerning[left][right][1];// * (h / (float)fmap->size);
1110 //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
1112 if (!Font_SetSize(font, w, h))
1114 // this deserves an error message
1115 Con_Printf("Failed to get kerning for %s\n", font->name);
1118 ul = qFT_Get_Char_Index(font->face, left);
1119 ur = qFT_Get_Char_Index(font->face, right);
1120 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
1122 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);
1123 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);
1127 if (!Font_SetSize(font, fmap->intSize, fmap->intSize))
1129 // this deserves an error message
1130 Con_Printf(CON_ERROR "Failed to get kerning for %s\n", font->name);
1133 ul = qFT_Get_Char_Index((FT_Face)font->face, left);
1134 ur = qFT_Get_Char_Index((FT_Face)font->face, right);
1135 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
1137 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);// * (w / (float)fmap->size);
1138 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);// * (h / (float)fmap->size);
1145 qbool Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
1147 return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
1150 // this is used to gracefully unload a map chain; the passed map
1151 // needs not necessarily be a startmap, so maps ahead of it can be kept
1152 static void UnloadMapChain(ft2_font_map_t *map)
1155 ft2_font_map_t *nextmap;
1156 // these may only be in a startmap
1157 if (map->kerning != NULL)
1158 Mem_Free(map->kerning);
1159 if (map->incmap != NULL)
1161 for (i = 0; i < FONT_CHARS_PER_LINE; ++i)
1162 if (map->incmap->data_tier1[i] != NULL)
1163 Mem_Free(map->incmap->data_tier1[i]);
1166 for (i = 0; i < FONT_CHAR_LINES; ++i)
1167 if (map->incmap->data_tier2[i] != NULL)
1168 Mem_Free(map->incmap->data_tier2[i]);
1171 Mem_Free(map->incmap);
1177 //Draw_FreePic(map->pic); // FIXME: refcounting needed...
1180 nextmap = map->next;
1186 void Font_UnloadFont(ft2_font_t *font)
1192 Font_UnloadFont(font->next);
1194 if (font->attachments && font->attachmentcount)
1196 for (i = 0; i < (int)font->attachmentcount; ++i) {
1197 if (font->attachments[i].data)
1198 fontfilecache_Free(font->attachments[i].data);
1200 Mem_Free(font->attachments);
1201 font->attachmentcount = 0;
1202 font->attachments = NULL;
1204 for (i = 0; i < MAX_FONT_SIZES; ++i)
1206 if (font->font_maps[i])
1208 UnloadMapChain(font->font_maps[i]);
1209 font->font_maps[i] = NULL;
1212 #ifndef DP_FREETYPE_STATIC
1215 if (!r_font_disable_freetype.integer)
1220 qFT_Done_Face((FT_Face)font->face);
1225 fontfilecache_Free(font->data);
1230 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size)
1232 float intSize = size;
1235 if (!Font_SetSize(font, intSize, intSize))
1237 Con_Printf(CON_ERROR "ERROR: can't set size for font %s: %f ((%f))\n", font->name, size, intSize);
1240 if ((fontface->size->metrics.height>>6) <= size)
1244 Con_Printf(CON_ERROR "ERROR: no appropriate size found for font %s: %f\n", font->name, size);
1251 // helper inline functions for incmap_post_process:
1253 static inline void update_pic_for_fontmap(ft2_font_map_t *fontmap, const char *identifier,
1254 int width, int height, unsigned char *data)
1256 fontmap->pic = Draw_NewPic(identifier, width, height, data, TEXTYPE_RGBA,
1257 TEXF_ALPHA | TEXF_CLAMP | (r_font_compress.integer > 0 ? TEXF_COMPRESS : 0));
1260 // glyphs' texture coords needs to be fixed when merging to bigger texture
1261 static inline void transform_glyph_coords(glyph_slot_t *glyph, float shiftx, float shifty, float scalex, float scaley)
1263 glyph->txmin = glyph->txmin * scalex + shiftx;
1264 glyph->txmax = glyph->txmax * scalex + shiftx;
1265 glyph->tymin = glyph->tymin * scaley + shifty;
1266 glyph->tymax = glyph->tymax * scaley + shifty;
1268 #define fix_glyph_coords_tier1(glyph, order) transform_glyph_coords(glyph, order / (float)FONT_CHARS_PER_LINE, 0.0f, 1.0f / (float)FONT_CHARS_PER_LINE, 1.0f)
1269 #define fix_glyph_coords_tier2(glyph, order) transform_glyph_coords(glyph, 0.0f, order / (float)FONT_CHARS_PER_LINE, 1.0f, 1.0f / (float)FONT_CHARS_PER_LINE)
1271 // pull glyph things from sourcemap to targetmap
1272 static inline void merge_single_map(ft2_font_map_t *targetmap, int targetindex, ft2_font_map_t *sourcemap, int sourceindex)
1274 targetmap->glyphs[targetindex] = sourcemap->glyphs[sourceindex];
1275 targetmap->glyphchars[targetindex] = sourcemap->glyphchars[sourceindex];
1278 #define calc_data_arguments(w, h) \
1279 width = startmap->glyphSize * w; \
1280 height = startmap->glyphSize * h; \
1281 pitch = width * bytes_per_pixel; \
1282 datasize = height * pitch;
1284 // do incremental map process
1285 static inline void incmap_post_process(font_incmap_t *incmap, Uchar ch,
1286 unsigned char *data, ft2_font_map_t **outmap, int *outmapch)
1288 #define bytes_per_pixel 4
1290 int index, targetmap_at;
1291 // where will the next `data` be placed
1292 int tier1_data_index, tier2_data_index;
1293 // metrics of data to manipulate
1294 int width, height, pitch, datasize;
1296 unsigned char *newdata, *chunk;
1297 ft2_font_map_t *startmap, *targetmap, *currentmap;
1298 #define M FONT_CHARS_PER_LINE
1299 #define N FONT_CHAR_LINES
1301 startmap = incmap->fontmap;
1302 index = incmap->charcount;
1303 tier1_data_index = index % M;
1304 tier2_data_index = incmap->tier1_merged;
1306 incmap->data_tier1[tier1_data_index] = data;
1308 if (index % M == M - 1)
1310 // tier 1 reduction, pieces to line
1311 calc_data_arguments(1, 1);
1312 targetmap_at = incmap->tier2_merged + incmap->tier1_merged;
1313 targetmap = startmap;
1314 for (i = 0; i < targetmap_at; ++i)
1315 targetmap = targetmap->next;
1316 currentmap = targetmap;
1317 newdata = (unsigned char *)Mem_Alloc(font_mempool, datasize * M);
1318 for (i = 0; i < M; ++i)
1320 chunk = incmap->data_tier1[i];
1323 for (y = 0; y < datasize; y += pitch)
1324 for (x = 0; x < pitch; ++x)
1325 newdata[y * M + i * pitch + x] = chunk[y + x];
1327 incmap->data_tier1[i] = NULL;
1328 merge_single_map(targetmap, i, currentmap, 0);
1329 fix_glyph_coords_tier1(&targetmap->glyphs[i], (float)i);
1330 currentmap = currentmap->next;
1332 update_pic_for_fontmap(targetmap, Draw_GetPicName(targetmap->pic), width * M, height, newdata);
1333 UnloadMapChain(targetmap->next);
1334 targetmap->next = NULL;
1335 incmap->data_tier2[tier2_data_index] = newdata;
1336 ++incmap->tier1_merged;
1337 incmap->tier1_merged %= M;
1338 incmap->newmap_start = INCMAP_START + targetmap_at + 1;
1339 // then give this merged map
1340 *outmap = targetmap;
1341 *outmapch = FONT_CHARS_PER_LINE - 1;
1343 if (index % (M * N) == M * N - 1)
1345 // tier 2 reduction, lines to full map
1346 calc_data_arguments(M, 1);
1347 targetmap_at = incmap->tier2_merged;
1348 targetmap = startmap;
1349 for (i = 0; i < targetmap_at; ++i)
1350 targetmap = targetmap->next;
1351 currentmap = targetmap;
1352 newdata = (unsigned char *)Mem_Alloc(font_mempool, datasize * N);
1353 for (i = 0; i < N; ++i)
1355 chunk = incmap->data_tier2[i];
1358 for (x = 0; x < datasize; ++x)
1359 newdata[i * datasize + x] = chunk[x];
1361 incmap->data_tier2[i] = NULL;
1362 for (j = 0; j < M; ++j)
1364 merge_single_map(targetmap, i * M + j, currentmap, j);
1365 fix_glyph_coords_tier2(&targetmap->glyphs[i * M + j], (float)i);
1367 currentmap = currentmap->next;
1369 update_pic_for_fontmap(targetmap, Draw_GetPicName(targetmap->pic), width, height * N, newdata);
1370 UnloadMapChain(targetmap->next);
1371 targetmap->next = NULL;
1373 ++incmap->tier2_merged;
1374 incmap->newmap_start = INCMAP_START + targetmap_at + 1;
1375 // then give this merged map
1376 *outmap = targetmap;
1377 *outmapch = FONT_CHARS_PER_MAP - 1;
1380 ++incmap->charcount;
1381 ++incmap->newmap_start;
1387 static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch,
1388 ft2_font_map_t **outmap, int *outmapch, qbool use_incmap)
1390 #define bytes_per_pixel 4
1392 char map_identifier[MAX_QPATH];
1393 unsigned long map_startglyph = _ch / FONT_CHARS_PER_MAP * FONT_CHARS_PER_MAP;
1394 unsigned char *data = NULL;
1395 FT_ULong ch = 0, mapch = 0;
1398 FT_Int32 load_flags;
1399 int gpad_l, gpad_r, gpad_t, gpad_b;
1402 int width, height, datasize;
1403 int glyph_row, glyph_column;
1405 int chars_per_line = FONT_CHARS_PER_LINE;
1406 int char_lines = FONT_CHAR_LINES;
1407 int chars_per_map = FONT_CHARS_PER_MAP;
1409 ft2_font_t *usefont;
1410 ft2_font_map_t *map, *next;
1411 font_incmap_t *incmap;
1415 incmap = mapstart->incmap;
1418 // only render one character in this map;
1419 // such small maps will be merged together later in `incmap_post_process`
1420 chars_per_line = char_lines = chars_per_map = 1;
1421 // and the index is incremental
1422 map_startglyph = incmap ? incmap->newmap_start : INCMAP_START;
1425 if (font->image_font)
1426 fontface = (FT_Face)font->next->face;
1428 fontface = (FT_Face)font->face;
1430 switch(font->settings->antialias)
1433 switch(font->settings->hinting)
1436 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1440 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1444 load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1450 switch(font->settings->hinting)
1453 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1456 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
1459 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1463 load_flags = FT_LOAD_TARGET_NORMAL;
1469 //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
1471 if (font->image_font && mapstart->intSize < 0)
1472 mapstart->intSize = mapstart->size;
1473 if (mapstart->intSize < 0)
1476 mapstart->intSize = mapstart->size;
1479 if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1481 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
1484 if ((fontface->size->metrics.height>>6) <= mapstart->size)
1486 if (mapstart->intSize < 2)
1488 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
1491 --mapstart->intSize;
1494 if ((mapstart->intSize = Font_SearchSize(font, fontface, mapstart->size)) <= 0)
1496 Con_DPrintf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
1499 if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1501 Con_Printf(CON_ERROR "ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
1505 map = (ft2_font_map_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
1508 Con_Printf(CON_ERROR "ERROR: Out of memory when allowcating fontmap for %s\n", font->name);
1512 map->start = map_startglyph;
1514 // create a unique name for this map, then we will use it to make a unique cachepic_t to avoid redundant textures
1516 dpsnprintf(map_identifier, sizeof(map_identifier),
1517 "%s_cache_%g_%d_%g_%g_%g_%g_%g_%u_%lx",
1519 (double) mapstart->intSize,
1521 (double) font->settings->blur,
1522 (double) font->settings->outline,
1523 (double) font->settings->shadowx,
1524 (double) font->settings->shadowy,
1525 (double) font->settings->shadowz,
1526 (unsigned) map_startglyph,
1527 // add pointer as a unique part to avoid earlier incmaps' state being trashed
1528 use_incmap ? (unsigned long)mapstart : 0x0);
1531 * note 1: it appears that different font instances may have the same metrics, causing this pic being overwritten
1532 * will use startmap's pointer as a unique part to avoid earlier incmaps' dynamic pics being trashed
1533 * note 2: if this identifier is made too long, significient performance drop will take place
1534 * note 3: blur/outline/shadow are per-font settings, so a pointer to startmap & map size
1535 * already made these unique, hence they are omitted
1536 * note 4: font_diskcache is removed, this name can be less meaningful
1538 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%g_%p_%u",
1539 font->name, mapstart->intSize, mapstart, (unsigned) map_startglyph);
1541 // create a cachepic_t from the data now, or reuse an existing one
1542 if (developer_font.integer)
1543 Con_Printf("Generating font map %s (size: %.1f MB)\n", map_identifier, mapstart->glyphSize * (256 * 4 / 1048576.0) * mapstart->glyphSize);
1545 Font_Postprocess(font, NULL, 0, bytes_per_pixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
1547 // copy over the information
1548 map->size = mapstart->size;
1549 map->intSize = mapstart->intSize;
1550 map->glyphSize = mapstart->glyphSize;
1551 map->sfx = mapstart->sfx;
1552 map->sfy = mapstart->sfy;
1554 width = map->glyphSize * chars_per_line;
1555 height = map->glyphSize * char_lines;
1556 pitch = width * bytes_per_pixel;
1557 datasize = height * pitch;
1558 data = (unsigned char *)Mem_Alloc(font_mempool, datasize);
1561 Con_Printf(CON_ERROR "ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
1568 if (mapstart->incmap == NULL)
1571 incmap = mapstart->incmap = (font_incmap_t *)Mem_Alloc(font_mempool, sizeof(font_incmap_t));
1574 Con_Printf(CON_ERROR "ERROR: Out of memory when allowcating incremental fontmap for %s\n", font->name);
1577 // this will be the startmap of incmap
1578 incmap->fontmap = map;
1579 incmap->newmap_start = INCMAP_START;
1583 // new maps for incmap shall always be the last one
1584 next = incmap->fontmap;
1585 while (next->next != NULL)
1592 // insert this normal map
1593 next = use_incmap ? incmap->fontmap : mapstart;
1594 while(next->next && next->next->start < map->start)
1596 map->next = next->next;
1600 // initialize as white texture with zero alpha
1602 while (tp < datasize)
1604 if (bytes_per_pixel == 4)
1615 ch = (FT_ULong)(use_incmap ? _ch : map->start);
1619 FT_ULong glyphIndex;
1623 unsigned char *imagedata = NULL, *dst, *src;
1624 glyph_slot_t *mapglyph;
1626 int pad_l, pad_r, pad_t, pad_b;
1628 if (developer_font.integer)
1629 Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
1631 map->glyphchars[mapch] = (Uchar)ch;
1635 imagedata = data + glyph_row * pitch * map->glyphSize + glyph_column * map->glyphSize * bytes_per_pixel;
1636 imagedata += gpad_t * pitch + gpad_l * bytes_per_pixel;
1638 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
1639 // we need the glyphIndex
1640 face = (FT_Face)font->face;
1642 if (font->image_font && mapch == ch && img_fontmap[mapch])
1644 map->glyphs[mapch].image = true;
1647 glyphIndex = qFT_Get_Char_Index(face, ch);
1648 if (glyphIndex == 0)
1650 // by convention, 0 is the "missing-glyph"-glyph
1651 // try to load from a fallback font
1652 for(usefont = font->next; usefont != NULL; usefont = usefont->next)
1654 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
1657 face = (FT_Face)usefont->face;
1658 glyphIndex = qFT_Get_Char_Index(face, ch);
1659 if (glyphIndex == 0)
1661 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1668 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1669 // now we let it use the "missing-glyph"-glyph
1670 face = (FT_Face)font->face;
1678 face = (FT_Face)font->face;
1679 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1682 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1683 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1688 glyph = face->glyph;
1689 bmp = &glyph->bitmap;
1694 if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
1695 Con_Printf(CON_WARN "WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
1696 if (w > map->glyphSize)
1697 w = map->glyphSize - gpad_l - gpad_r;
1698 if (h > map->glyphSize)
1704 switch (bmp->pixel_mode)
1706 case FT_PIXEL_MODE_MONO:
1707 if (developer_font.integer)
1708 Con_DPrint("glyphinfo: Pixel Mode: MONO\n");
1710 case FT_PIXEL_MODE_GRAY2:
1711 if (developer_font.integer)
1712 Con_DPrint("glyphinfo: Pixel Mode: GRAY2\n");
1714 case FT_PIXEL_MODE_GRAY4:
1715 if (developer_font.integer)
1716 Con_DPrint("glyphinfo: Pixel Mode: GRAY4\n");
1718 case FT_PIXEL_MODE_GRAY:
1719 if (developer_font.integer)
1720 Con_DPrint("glyphinfo: Pixel Mode: GRAY\n");
1723 if (developer_font.integer)
1724 Con_DPrintf("glyphinfo: Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1726 Con_Printf(CON_ERROR "ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1729 for (y = 0; y < h; ++y)
1731 dst = imagedata + y * pitch;
1732 src = bmp->buffer + y * bmp->pitch;
1734 switch (bmp->pixel_mode)
1736 case FT_PIXEL_MODE_MONO:
1737 dst += bytes_per_pixel - 1; // shift to alpha byte
1738 for (x = 0; x < bmp->width; x += 8)
1740 unsigned char c = *src++;
1741 *dst = 255 * !!((c & 0x80) >> 7); dst += bytes_per_pixel;
1742 *dst = 255 * !!((c & 0x40) >> 6); dst += bytes_per_pixel;
1743 *dst = 255 * !!((c & 0x20) >> 5); dst += bytes_per_pixel;
1744 *dst = 255 * !!((c & 0x10) >> 4); dst += bytes_per_pixel;
1745 *dst = 255 * !!((c & 0x08) >> 3); dst += bytes_per_pixel;
1746 *dst = 255 * !!((c & 0x04) >> 2); dst += bytes_per_pixel;
1747 *dst = 255 * !!((c & 0x02) >> 1); dst += bytes_per_pixel;
1748 *dst = 255 * !!((c & 0x01) >> 0); dst += bytes_per_pixel;
1751 case FT_PIXEL_MODE_GRAY2:
1752 dst += bytes_per_pixel - 1; // shift to alpha byte
1753 for (x = 0; x < bmp->width; x += 4)
1755 unsigned char c = *src++;
1756 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytes_per_pixel;
1757 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytes_per_pixel;
1758 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytes_per_pixel;
1759 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytes_per_pixel;
1762 case FT_PIXEL_MODE_GRAY4:
1763 dst += bytes_per_pixel - 1; // shift to alpha byte
1764 for (x = 0; x < bmp->width; x += 2)
1766 unsigned char c = *src++;
1767 *dst = ( ((c & 0xF0) >> 4) * 0x11); dst += bytes_per_pixel;
1768 *dst = ( ((c & 0x0F) ) * 0x11); dst += bytes_per_pixel;
1771 case FT_PIXEL_MODE_GRAY:
1772 // in this case pitch should equal width
1773 for (tp = 0; tp < bmp->pitch; ++tp)
1774 dst[(bytes_per_pixel - 1) + tp*bytes_per_pixel] = src[tp]; // copy the grey value into the alpha bytes
1776 //memcpy((void*)dst, (void*)src, bmp->pitch);
1777 //dst += bmp->pitch;
1788 Font_Postprocess(font, imagedata, pitch, bytes_per_pixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1796 Font_Postprocess(font, NULL, pitch, bytes_per_pixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1800 // now fill map->glyphs[ch - map->start]
1801 mapglyph = &map->glyphs[mapch];
1805 // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1807 double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1808 //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1809 double advance = (glyph->advance.x / 64.0) / map->size;
1810 //double mWidth = (glyph->metrics.width >> 6) / map->size;
1811 //double mHeight = (glyph->metrics.height >> 6) / map->size;
1813 mapglyph->txmin = ( (double)(glyph_column * map->glyphSize) + (double)(gpad_l - pad_l) ) / ( (double)(map->glyphSize * chars_per_line) );
1814 mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * chars_per_line) );
1815 mapglyph->tymin = ( (double)(glyph_row * map->glyphSize) + (double)(gpad_r - pad_r) ) / ( (double)(map->glyphSize * char_lines) );
1816 mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * char_lines) );
1817 //mapglyph->vxmin = bearingX;
1818 //mapglyph->vxmax = bearingX + mWidth;
1819 mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
1820 mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
1821 //mapglyph->vymin = -bearingY;
1822 //mapglyph->vymax = mHeight - bearingY;
1823 mapglyph->vymin = (-glyph->bitmap_top - pad_t) / map->size;
1824 mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
1825 //Con_Printf("dpi = %f %f (%f %d) %d %d\n", bmp->width / (mapglyph->vxmax - mapglyph->vxmin), bmp->rows / (mapglyph->vymax - mapglyph->vymin), map->size, map->glyphSize, (int)fontface->size->metrics.x_ppem, (int)fontface->size->metrics.y_ppem);
1826 //mapglyph->advance_x = advance * usefont->size;
1827 //mapglyph->advance_x = advance;
1828 mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1829 mapglyph->advance_y = 0;
1831 if (developer_font.integer)
1833 Con_DPrintf("glyphinfo: Glyph: %lu at (%i, %i)\n", (unsigned long)ch, glyph_column, glyph_row);
1834 Con_DPrintf("glyphinfo: %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1835 if (ch >= 32 && ch <= 128)
1836 Con_DPrintf("glyphinfo: Character: %c\n", (int)ch);
1837 Con_DPrintf("glyphinfo: Vertex info:\n");
1838 Con_DPrintf("glyphinfo: X: ( %f -- %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1839 Con_DPrintf("glyphinfo: Y: ( %f -- %f )\n", mapglyph->vymin, mapglyph->vymax);
1840 Con_DPrintf("glyphinfo: Texture info:\n");
1841 Con_DPrintf("glyphinfo: S: ( %f -- %f )\n", mapglyph->txmin, mapglyph->txmax);
1842 Con_DPrintf("glyphinfo: T: ( %f -- %f )\n", mapglyph->tymin, mapglyph->tymax);
1843 Con_DPrintf("glyphinfo: Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1846 map->glyphs[mapch].image = false;
1849 if ((int)mapch == chars_per_map)
1851 if (++glyph_column % chars_per_line == 0)
1858 // update the pic returned by Draw_CachePic_Flags earlier to contain our texture
1859 update_pic_for_fontmap(map, map_identifier, width, height, data);
1861 if (!Draw_IsPicLoaded(map->pic))
1863 // if the first try isn't successful, keep it with a broken texture
1864 // otherwise we retry to load it every single frame where ft2 rendering is used
1865 // this would be bad...
1866 // only `data' must be freed
1867 Con_Printf(CON_ERROR "ERROR: Failed to generate texture for font %s size %f map %lu\n",
1868 font->name, mapstart->size, map_startglyph);
1876 // data will be kept in incmap for being merged later, freed afterward
1877 incmap_post_process(incmap, _ch, data, outmap, outmapch);
1883 if (outmapch != NULL)
1884 *outmapch = _ch - map->start;
1890 static qbool legacy_font_loading_api_alerted = false;
1891 static inline void alert_legacy_font_api(const char *name)
1893 if (!legacy_font_loading_api_alerted)
1895 Con_DPrintf(CON_WARN "Warning: You are using an legacy API '%s', which have certain limitations; please use 'Font_GetMapForChar' instead\n", name);
1896 legacy_font_loading_api_alerted = true;
1900 // legacy font API, please use `Font_GetMapForChar` instead
1901 qbool Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar ch, ft2_font_map_t **outmap)
1903 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1905 // the first map must have been loaded already
1906 if (!font->font_maps[map_index])
1908 alert_legacy_font_api("Font_LoadMapForIndex");
1909 return Font_LoadMap(font, font->font_maps[map_index], ch, outmap, NULL, false);
1912 // legacy font API. please use `Font_GetMapForChar` instead
1913 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1915 ft2_font_map_t *map = start;
1916 while (map && map->start + FONT_CHARS_PER_MAP <= ch)
1918 if (map && map->start > ch)
1920 alert_legacy_font_api("FontMap_FindForChar");
1924 static inline qbool should_use_incmap(Uchar ch)
1927 // optimize: a simple check logic for usual conditions
1928 if (ch < unicode_bigblocks[0])
1930 if (r_font_disable_incmaps.integer == 1)
1932 for (i = 0; i < (int)(sizeof(unicode_bigblocks) / sizeof(Uchar)); i += 2)
1933 if (unicode_bigblocks[i] <= ch && ch <= unicode_bigblocks[i + 1])
1938 static inline qbool get_char_from_incmap(ft2_font_map_t *map, Uchar ch, ft2_font_map_t **outmap, int *outmapch)
1941 font_incmap_t *incmap;
1943 incmap = map->incmap;
1948 map = incmap->fontmap;
1951 for (i = 0; i < FONT_CHARS_PER_MAP; ++i)
1953 if (map->glyphchars[i] == ch)
1959 else if (map->glyphchars[i] == 0)
1960 // this tier0/tier1 map ends here
1970 * Query for or load a font map for a character, with the character's place on it.
1971 * Supports the incremental map mechanism; returning if the operation is done successfully
1973 qbool Font_GetMapForChar(ft2_font_t *font, int map_index, Uchar ch, ft2_font_map_t **outmap, int *outmapch)
1976 ft2_font_map_t *map;
1979 map = Font_MapForIndex(font, map_index);
1981 // optimize: the first map must have been loaded already
1982 if (ch < FONT_CHARS_PER_MAP)
1989 // search for the character
1991 use_incmap = should_use_incmap(ch);
1996 *outmapch = ch % FONT_CHARS_PER_MAP;
1997 while (map && map->start + FONT_CHARS_PER_MAP <= ch)
1999 if (map && map->start <= ch)
2005 else if (get_char_from_incmap(map, ch, outmap, outmapch))
2009 // so no appropriate map was found, load one
2011 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
2013 return Font_LoadMap(font, font->font_maps[map_index], ch, outmap, outmapch, use_incmap);