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 characters are "spreaded", and ordinary maps will
36 * waste huge amount of resources rendering/caching unused glyphs.
38 * So, a new design is introduced to solve the problem:
39 * incremental maps, in which only used glyphs are stored.
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_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!"};
57 cvar_t r_font_kerning = {CF_CLIENT | CF_ARCHIVE, "r_font_kerning", "1", "Use kerning if available"};
58 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"};
59 cvar_t r_font_compress = {CF_CLIENT | CF_ARCHIVE, "r_font_compress", "0", "use texture compression on font textures to save video memory"};
60 cvar_t r_font_nonpoweroftwo = {CF_CLIENT | CF_ARCHIVE, "r_font_nonpoweroftwo", "1", "use nonpoweroftwo textures for font (saves memory, potentially slower)"};
61 cvar_t developer_font = {CF_CLIENT | CF_ARCHIVE, "developer_font", "0", "prints debug messages about fonts"};
63 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"};
65 #ifndef DP_FREETYPE_STATIC
68 ================================================================================
69 Function definitions. Taken from the freetype2 headers.
70 ================================================================================
75 (*qFT_Init_FreeType)( FT_Library *alibrary );
77 (*qFT_Done_FreeType)( FT_Library library );
80 (*qFT_New_Face)( FT_Library library,
81 const char* filepathname,
86 (*qFT_New_Memory_Face)( FT_Library library,
87 const FT_Byte* file_base,
92 (*qFT_Done_Face)( FT_Face face );
94 (*qFT_Select_Size)( FT_Face face,
95 FT_Int strike_index );
97 (*qFT_Request_Size)( FT_Face face,
98 FT_Size_Request req );
100 (*qFT_Set_Char_Size)( FT_Face face,
101 FT_F26Dot6 char_width,
102 FT_F26Dot6 char_height,
103 FT_UInt horz_resolution,
104 FT_UInt vert_resolution );
105 FT_EXPORT( FT_Error )
106 (*qFT_Set_Pixel_Sizes)( FT_Face face,
108 FT_UInt pixel_height );
109 FT_EXPORT( FT_Error )
110 (*qFT_Load_Glyph)( FT_Face face,
112 FT_Int32 load_flags );
113 FT_EXPORT( FT_Error )
114 (*qFT_Load_Char)( FT_Face face,
116 FT_Int32 load_flags );
118 (*qFT_Get_Char_Index)( FT_Face face,
120 FT_EXPORT( FT_Error )
121 (*qFT_Render_Glyph)( FT_GlyphSlot slot,
122 FT_Render_Mode render_mode );
123 FT_EXPORT( FT_Error )
124 (*qFT_Get_Kerning)( FT_Face face,
128 FT_Vector *akerning );
129 FT_EXPORT( FT_Error )
130 (*qFT_Attach_Stream)( FT_Face face,
131 FT_Open_Args* parameters );
133 ================================================================================
134 Support for dynamically loading the FreeType2 library
135 ================================================================================
138 static dllfunction_t ft2funcs[] =
140 {"FT_Init_FreeType", (void **) &qFT_Init_FreeType},
141 {"FT_Done_FreeType", (void **) &qFT_Done_FreeType},
142 //{"FT_New_Face", (void **) &qFT_New_Face},
143 {"FT_New_Memory_Face", (void **) &qFT_New_Memory_Face},
144 {"FT_Done_Face", (void **) &qFT_Done_Face},
145 {"FT_Select_Size", (void **) &qFT_Select_Size},
146 {"FT_Request_Size", (void **) &qFT_Request_Size},
147 {"FT_Set_Char_Size", (void **) &qFT_Set_Char_Size},
148 {"FT_Set_Pixel_Sizes", (void **) &qFT_Set_Pixel_Sizes},
149 {"FT_Load_Glyph", (void **) &qFT_Load_Glyph},
150 {"FT_Load_Char", (void **) &qFT_Load_Char},
151 {"FT_Get_Char_Index", (void **) &qFT_Get_Char_Index},
152 {"FT_Render_Glyph", (void **) &qFT_Render_Glyph},
153 {"FT_Get_Kerning", (void **) &qFT_Get_Kerning},
154 {"FT_Attach_Stream", (void **) &qFT_Attach_Stream},
158 /// Handle for FreeType2 DLL
159 static dllhandle_t ft2_dll = NULL;
163 FT_EXPORT( FT_Error )
164 (FT_Init_FreeType)( FT_Library *alibrary );
165 FT_EXPORT( FT_Error )
166 (FT_Done_FreeType)( FT_Library library );
168 FT_EXPORT( FT_Error )
169 (FT_New_Face)( FT_Library library,
170 const char* filepathname,
174 FT_EXPORT( FT_Error )
175 (FT_New_Memory_Face)( FT_Library library,
176 const FT_Byte* file_base,
180 FT_EXPORT( FT_Error )
181 (FT_Done_Face)( FT_Face face );
182 FT_EXPORT( FT_Error )
183 (FT_Select_Size)( FT_Face face,
184 FT_Int strike_index );
185 FT_EXPORT( FT_Error )
186 (FT_Request_Size)( FT_Face face,
187 FT_Size_Request req );
188 FT_EXPORT( FT_Error )
189 (FT_Set_Char_Size)( FT_Face face,
190 FT_F26Dot6 char_width,
191 FT_F26Dot6 char_height,
192 FT_UInt horz_resolution,
193 FT_UInt vert_resolution );
194 FT_EXPORT( FT_Error )
195 (FT_Set_Pixel_Sizes)( FT_Face face,
197 FT_UInt pixel_height );
198 FT_EXPORT( FT_Error )
199 (FT_Load_Glyph)( FT_Face face,
201 FT_Int32 load_flags );
202 FT_EXPORT( FT_Error )
203 (FT_Load_Char)( FT_Face face,
205 FT_Int32 load_flags );
207 (FT_Get_Char_Index)( FT_Face face,
209 FT_EXPORT( FT_Error )
210 (FT_Render_Glyph)( FT_GlyphSlot slot,
211 FT_Render_Mode render_mode );
212 FT_EXPORT( FT_Error )
213 (FT_Get_Kerning)( FT_Face face,
217 FT_Vector *akerning );
218 FT_EXPORT( FT_Error )
219 (FT_Attach_Stream)( FT_Face face,
220 FT_Open_Args* parameters );
222 #define qFT_Init_FreeType FT_Init_FreeType
223 #define qFT_Done_FreeType FT_Done_FreeType
224 //#define qFT_New_Face FT_New_Face
225 #define qFT_New_Memory_Face FT_New_Memory_Face
226 #define qFT_Done_Face FT_Done_Face
227 #define qFT_Select_Size FT_Select_Size
228 #define qFT_Request_Size FT_Request_Size
229 #define qFT_Set_Char_Size FT_Set_Char_Size
230 #define qFT_Set_Pixel_Sizes FT_Set_Pixel_Sizes
231 #define qFT_Load_Glyph FT_Load_Glyph
232 #define qFT_Load_Char FT_Load_Char
233 #define qFT_Get_Char_Index FT_Get_Char_Index
234 #define qFT_Render_Glyph FT_Render_Glyph
235 #define qFT_Get_Kerning FT_Get_Kerning
236 #define qFT_Attach_Stream FT_Attach_Stream
240 /// Memory pool for fonts
241 static mempool_t *font_mempool= NULL;
243 /// FreeType library handle
244 static FT_Library font_ft2lib = NULL;
246 #define POSTPROCESS_MAXRADIUS 8
249 unsigned char *buf, *buf2;
250 int bufsize, bufwidth, bufheight, bufpitch;
251 float blur, outline, shadowx, shadowy, shadowz;
252 int padding_t, padding_b, padding_l, padding_r, blurpadding_lt, blurpadding_rb, outlinepadding_t, outlinepadding_b, outlinepadding_l, outlinepadding_r;
253 unsigned char circlematrix[2*POSTPROCESS_MAXRADIUS+1][2*POSTPROCESS_MAXRADIUS+1];
254 unsigned char gausstable[2*POSTPROCESS_MAXRADIUS+1];
257 static font_postprocess_t pp;
259 typedef struct fontfilecache_s
264 char path[MAX_QPATH];
267 #define MAX_FONTFILES 8
268 static fontfilecache_t fontfiles[MAX_FONTFILES];
269 static const unsigned char *fontfilecache_LoadFile(const char *path, qbool quiet, fs_offset_t *filesizepointer)
274 for(i = 0; i < MAX_FONTFILES; ++i)
276 if(fontfiles[i].refcount > 0)
277 if(!strcmp(path, fontfiles[i].path))
279 *filesizepointer = fontfiles[i].len;
280 ++fontfiles[i].refcount;
281 return fontfiles[i].buf;
285 buf = FS_LoadFile(path, font_mempool, quiet, filesizepointer);
288 for(i = 0; i < MAX_FONTFILES; ++i)
289 if(fontfiles[i].refcount <= 0)
291 dp_strlcpy(fontfiles[i].path, path, sizeof(fontfiles[i].path));
292 fontfiles[i].len = *filesizepointer;
293 fontfiles[i].buf = buf;
294 fontfiles[i].refcount = 1;
301 static void fontfilecache_Free(const unsigned char *buf)
304 for(i = 0; i < MAX_FONTFILES; ++i)
306 if(fontfiles[i].refcount > 0)
307 if(fontfiles[i].buf == buf)
309 if(--fontfiles[i].refcount <= 0)
311 Mem_Free(fontfiles[i].buf);
312 fontfiles[i].buf = NULL;
317 // if we get here, it used regular allocation
318 Mem_Free((void *) buf);
320 static void fontfilecache_FreeAll(void)
323 for(i = 0; i < MAX_FONTFILES; ++i)
325 if(fontfiles[i].refcount > 0)
326 Mem_Free(fontfiles[i].buf);
327 fontfiles[i].buf = NULL;
328 fontfiles[i].refcount = 0;
336 Unload the FreeType2 DLL
339 void Font_CloseLibrary (void)
341 fontfilecache_FreeAll();
343 Mem_FreePool(&font_mempool);
344 if (font_ft2lib && qFT_Done_FreeType)
346 qFT_Done_FreeType(font_ft2lib);
349 #ifndef DP_FREETYPE_STATIC
350 Sys_FreeLibrary (&ft2_dll);
359 Try to load the FreeType2 DLL
362 qbool Font_OpenLibrary (void)
364 #ifndef DP_FREETYPE_STATIC
365 const char* dllnames [] =
370 #elif defined(MACOSX)
371 "libfreetype.6.dylib",
381 if (r_font_disable_freetype.integer)
384 #ifndef DP_FREETYPE_STATIC
390 if (!Sys_LoadDependency (dllnames, &ft2_dll, ft2funcs))
400 Initialize the freetype2 font subsystem
404 void font_start(void)
406 if (!Font_OpenLibrary())
409 if (qFT_Init_FreeType(&font_ft2lib))
411 Con_Print(CON_ERROR "ERROR: Failed to initialize the FreeType2 library!\n");
416 font_mempool = Mem_AllocPool("FONT", 0, NULL);
419 Con_Print(CON_ERROR "ERROR: Failed to allocate FONT memory pool!\n");
425 void font_shutdown(void)
428 for (i = 0; i < dp_fonts.maxsize; ++i)
430 if (dp_fonts.f[i].ft2)
432 Font_UnloadFont(dp_fonts.f[i].ft2);
433 dp_fonts.f[i].ft2 = NULL;
439 void font_newmap(void)
445 Cvar_RegisterVariable(&r_font_nonpoweroftwo);
446 Cvar_RegisterVariable(&r_font_disable_freetype);
447 Cvar_RegisterVariable(&r_font_size_snapping);
448 Cvar_RegisterVariable(&r_font_kerning);
449 Cvar_RegisterVariable(&r_font_diskcache);
450 Cvar_RegisterVariable(&r_font_compress);
451 Cvar_RegisterVariable(&developer_font);
453 Cvar_RegisterVariable(&r_font_disable_incmaps);
455 // let's open it at startup already
460 ================================================================================
461 Implementation of a more or less lazy font loading and rendering code.
462 ================================================================================
465 #include "ft2_fontdefs.h"
467 ft2_font_t *Font_Alloc(void)
469 #ifndef DP_FREETYPE_STATIC
472 if (r_font_disable_freetype.integer)
475 return (ft2_font_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_t));
478 static qbool Font_Attach(ft2_font_t *font, ft2_attachment_t *attachment)
480 ft2_attachment_t *na;
482 font->attachmentcount++;
483 na = (ft2_attachment_t*)Mem_Alloc(font_mempool, sizeof(font->attachments[0]) * font->attachmentcount);
486 if (font->attachments && font->attachmentcount > 1)
488 memcpy(na, font->attachments, sizeof(font->attachments[0]) * (font->attachmentcount - 1));
489 Mem_Free(font->attachments);
491 memcpy(na + sizeof(font->attachments[0]) * (font->attachmentcount - 1), attachment, sizeof(*attachment));
492 font->attachments = na;
496 float Font_VirtualToRealSize(float sz)
504 //vw = ((vid.width > 0) ? vid.width : vid_width.value);
505 vh = ((vid.height > 0) ? vid.height : vid_height.value);
506 // now try to scale to our actual size:
507 sn = sz * vh / vid_conheight.value;
509 if ( sn - (float)si >= 0.5 )
514 float Font_SnapTo(float val, float snapwidth)
516 return floor(val / snapwidth + 0.5f) * snapwidth;
519 static qbool Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font);
520 static qbool Font_LoadSize(ft2_font_t *font, float size, qbool check_only);
521 qbool Font_LoadFont(const char *name, dp_font_t *dpfnt)
524 ft2_font_t *ft2, *fbfont, *fb;
534 // check if a fallback font has been specified, if it has been, and the
535 // font fails to load, use the image font as main font
536 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
538 if (dpfnt->fallbacks[i][0])
542 if (!Font_LoadFile(name, dpfnt->req_face, &dpfnt->settings, ft2))
544 if (i >= MAX_FONT_FALLBACKS)
550 dp_strlcpy(ft2->name, name, sizeof(ft2->name));
551 ft2->image_font = true;
552 ft2->has_kerning = false;
556 ft2->image_font = false;
559 // attempt to load fallback fonts:
561 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
563 if (!dpfnt->fallbacks[i][0])
565 if (! (fb = Font_Alloc()) )
567 Con_Printf(CON_ERROR "Failed to allocate font for fallback %i of font %s\n", i, name);
571 if (!Font_LoadFile(dpfnt->fallbacks[i], dpfnt->fallback_faces[i], &dpfnt->settings, fb))
573 if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.tga", dpfnt->fallbacks[i])))
574 if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.png", dpfnt->fallbacks[i])))
575 if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.jpg", dpfnt->fallbacks[i])))
576 if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.pcx", dpfnt->fallbacks[i])))
577 Con_Printf(CON_ERROR "Failed to load font %s for fallback %i of font %s\n", dpfnt->fallbacks[i], i, name);
582 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
584 if (Font_LoadSize(fb, Font_VirtualToRealSize(dpfnt->req_sizes[s]), true))
589 Con_Printf(CON_ERROR "Failed to allocate font for fallback %i of font %s\n", i, name);
594 // at least one size of the fallback font loaded successfully
600 if (fbfont == ft2 && ft2->image_font)
602 // no fallbacks were loaded successfully:
609 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
611 if (Font_LoadSize(ft2, Font_VirtualToRealSize(dpfnt->req_sizes[s]), false))
616 // loading failed for every requested size
617 Font_UnloadFont(ft2);
623 //Con_Printf("%i sizes loaded\n", count);
628 static qbool Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font)
631 char filename[MAX_QPATH];
634 const unsigned char *data;
635 fs_offset_t datasize;
637 memset(font, 0, sizeof(*font));
639 if (!Font_OpenLibrary())
641 if (!r_font_disable_freetype.integer)
643 Con_Printf(CON_WARN "WARNING: can't open load font %s\n"
644 "You need the FreeType2 DLL to load font files\n",
650 font->settings = settings;
652 namelen = strlen(name);
653 if (namelen + 5 > sizeof(filename))
655 Con_Printf(CON_WARN "WARNING: too long font name. Cannot load this.\n");
659 // try load direct file
660 memcpy(filename, name, namelen+1);
661 data = fontfilecache_LoadFile(filename, false, &datasize);
665 memcpy(filename + namelen, ".ttf", 5);
666 data = fontfilecache_LoadFile(filename, false, &datasize);
671 memcpy(filename + namelen, ".otf", 5);
672 data = fontfilecache_LoadFile(filename, false, &datasize);
677 ft2_attachment_t afm;
679 memcpy(filename + namelen, ".pfb", 5);
680 data = fontfilecache_LoadFile(filename, false, &datasize);
684 memcpy(filename + namelen, ".afm", 5);
685 afm.data = fontfilecache_LoadFile(filename, false, &afm.size);
688 Font_Attach(font, &afm);
693 // FS_LoadFile being not-quiet should print an error :)
696 Con_DPrintf("Loading font %s face %i...\n", filename, _face);
698 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
699 if (status && _face != 0)
701 Con_Printf(CON_ERROR "Failed to load face %i of %s. Falling back to face 0\n", _face, name);
703 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
708 Con_Printf(CON_ERROR "ERROR: can't create face for %s\n"
709 "Error %i\n", // TODO: error strings
711 Font_UnloadFont(font);
715 // add the attachments
716 for (i = 0; i < font->attachmentcount; ++i)
719 memset(&args, 0, sizeof(args));
720 args.flags = FT_OPEN_MEMORY;
721 args.memory_base = (const FT_Byte*)font->attachments[i].data;
722 args.memory_size = font->attachments[i].size;
723 if (qFT_Attach_Stream((FT_Face)font->face, &args))
724 Con_Printf(CON_ERROR "Failed to add attachment %u to %s\n", (unsigned)i, font->name);
727 dp_strlcpy(font->name, name, sizeof(font->name));
728 font->image_font = false;
729 font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
733 static void Font_Postprocess_Update(ft2_font_t *fnt, int bpp, int w, int h)
736 float gausstable[2*POSTPROCESS_MAXRADIUS+1];
737 qbool need_gauss = (!pp.buf || pp.blur != fnt->settings->blur || pp.shadowz != fnt->settings->shadowz);
738 qbool need_circle = (!pp.buf || pp.outline != fnt->settings->outline || pp.shadowx != fnt->settings->shadowx || pp.shadowy != fnt->settings->shadowy);
739 pp.blur = fnt->settings->blur;
740 pp.outline = fnt->settings->outline;
741 pp.shadowx = fnt->settings->shadowx;
742 pp.shadowy = fnt->settings->shadowy;
743 pp.shadowz = fnt->settings->shadowz;
744 pp.outlinepadding_l = bound(0, ceil(pp.outline - pp.shadowx), POSTPROCESS_MAXRADIUS);
745 pp.outlinepadding_r = bound(0, ceil(pp.outline + pp.shadowx), POSTPROCESS_MAXRADIUS);
746 pp.outlinepadding_t = bound(0, ceil(pp.outline - pp.shadowy), POSTPROCESS_MAXRADIUS);
747 pp.outlinepadding_b = bound(0, ceil(pp.outline + pp.shadowy), POSTPROCESS_MAXRADIUS);
748 pp.blurpadding_lt = bound(0, ceil(pp.blur - pp.shadowz), POSTPROCESS_MAXRADIUS);
749 pp.blurpadding_rb = bound(0, ceil(pp.blur + pp.shadowz), POSTPROCESS_MAXRADIUS);
750 pp.padding_l = pp.blurpadding_lt + pp.outlinepadding_l;
751 pp.padding_r = pp.blurpadding_rb + pp.outlinepadding_r;
752 pp.padding_t = pp.blurpadding_lt + pp.outlinepadding_t;
753 pp.padding_b = pp.blurpadding_rb + pp.outlinepadding_b;
757 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
758 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));
759 for(x = -pp.blurpadding_rb; x <= pp.blurpadding_lt; ++x)
760 sum += gausstable[POSTPROCESS_MAXRADIUS+x];
761 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
762 pp.gausstable[POSTPROCESS_MAXRADIUS+x] = floor(gausstable[POSTPROCESS_MAXRADIUS+x] / sum * 255 + 0.5);
766 for(y = -POSTPROCESS_MAXRADIUS; y <= POSTPROCESS_MAXRADIUS; ++y)
767 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
769 float d = pp.outline + 1 - sqrt(pow(x + pp.shadowx, 2) + pow(y + pp.shadowy, 2));
770 pp.circlematrix[POSTPROCESS_MAXRADIUS+y][POSTPROCESS_MAXRADIUS+x] = (d >= 1) ? 255 : (d <= 0) ? 0 : floor(d * 255 + 0.5);
773 pp.bufwidth = w + pp.padding_l + pp.padding_r;
774 pp.bufheight = h + pp.padding_t + pp.padding_b;
775 pp.bufpitch = pp.bufwidth;
776 needed = pp.bufwidth * pp.bufheight;
777 if(!pp.buf || pp.bufsize < needed * 2)
781 pp.bufsize = needed * 4;
782 pp.buf = (unsigned char *)Mem_Alloc(font_mempool, pp.bufsize);
783 pp.buf2 = pp.buf + needed;
787 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)
791 // calculate gauss table
792 Font_Postprocess_Update(fnt, bpp, w, h);
797 // perform operation, not exceeding the passed padding values,
798 // but possibly reducing them
799 *pad_l = min(*pad_l, pp.padding_l);
800 *pad_r = min(*pad_r, pp.padding_r);
801 *pad_t = min(*pad_t, pp.padding_t);
802 *pad_b = min(*pad_b, pp.padding_b);
804 // outline the font (RGBA only)
805 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
807 // this is like mplayer subtitle rendering
808 // bbuffer, bitmap buffer: this is our font
809 // abuffer, alpha buffer: this is pp.buf
810 // tmp: this is pp.buf2
812 // create outline buffer
813 memset(pp.buf, 0, pp.bufwidth * pp.bufheight);
814 for(y = -*pad_t; y < h + *pad_b; ++y)
815 for(x = -*pad_l; x < w + *pad_r; ++x)
817 int x1 = max(-x, -pp.outlinepadding_r);
818 int y1 = max(-y, -pp.outlinepadding_b);
819 int x2 = min(pp.outlinepadding_l, w-1-x);
820 int y2 = min(pp.outlinepadding_t, h-1-y);
824 for(my = y1; my <= y2; ++my)
825 for(mx = x1; mx <= x2; ++mx)
827 cur = pp.circlematrix[POSTPROCESS_MAXRADIUS+my][POSTPROCESS_MAXRADIUS+mx] * (int)imagedata[(x+mx) * bpp + pitch * (y+my) + (bpp - 1)];
831 pp.buf[((x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t))] = (highest + 128) / 255;
834 // blur the outline buffer
835 if(pp.blur > 0 || pp.shadowz != 0)
838 for(y = 0; y < pp.bufheight; ++y)
839 for(x = 0; x < pp.bufwidth; ++x)
841 int x1 = max(-x, -pp.blurpadding_rb);
842 int x2 = min(pp.blurpadding_lt, pp.bufwidth-1-x);
845 for(mx = x1; mx <= x2; ++mx)
846 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+mx] * (int)pp.buf[(x+mx) + pp.bufpitch * y];
847 pp.buf2[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
851 for(y = 0; y < pp.bufheight; ++y)
852 for(x = 0; x < pp.bufwidth; ++x)
854 int y1 = max(-y, -pp.blurpadding_rb);
855 int y2 = min(pp.blurpadding_lt, pp.bufheight-1-y);
858 for(my = y1; my <= y2; ++my)
859 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+my] * (int)pp.buf2[x + pp.bufpitch * (y+my)];
860 pp.buf[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
864 // paste the outline below the font
865 for(y = -*pad_t; y < h + *pad_b; ++y)
866 for(x = -*pad_l; x < w + *pad_r; ++x)
868 unsigned char outlinealpha = pp.buf[(x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t)];
871 unsigned char oldalpha = imagedata[x * bpp + pitch * y + (bpp - 1)];
872 // a' = 1 - (1 - a1) (1 - a2)
873 unsigned char newalpha = 255 - ((255 - (int)outlinealpha) * (255 - (int)oldalpha)) / 255; // this is >= oldalpha
874 // c' = (a2 c2 - a1 a2 c1 + a1 c1) / a' = (a2 c2 + a1 (1 - a2) c1) / a'
875 unsigned char oldfactor = (255 * (int)oldalpha) / newalpha;
876 //unsigned char outlinefactor = ((255 - oldalpha) * (int)outlinealpha) / newalpha;
878 for(i = 0; i < bpp-1; ++i)
880 unsigned char c = imagedata[x * bpp + pitch * y + i];
881 c = (c * (int)oldfactor) / 255 /* + outlinecolor[i] * (int)outlinefactor */;
882 imagedata[x * bpp + pitch * y + i] = c;
884 imagedata[x * bpp + pitch * y + (bpp - 1)] = newalpha;
886 //imagedata[x * bpp + pitch * y + (bpp - 1)] |= 0x80;
892 // perform operation, not exceeding the passed padding values,
893 // but possibly reducing them
894 *pad_l = min(*pad_l, pp.padding_l);
895 *pad_r = min(*pad_r, pp.padding_r);
896 *pad_t = min(*pad_t, pp.padding_t);
897 *pad_b = min(*pad_b, pp.padding_b);
901 // just calculate parameters
902 *pad_l = pp.padding_l;
903 *pad_r = pp.padding_r;
904 *pad_t = pp.padding_t;
905 *pad_b = pp.padding_b;
909 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size);
910 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);
911 static qbool Font_LoadSize(ft2_font_t *font, float size, qbool check_only)
914 ft2_font_map_t *fmap, temp;
915 int gpad_l, gpad_r, gpad_t, gpad_b;
917 if (!(size > 0.001f && size < 1000.0f))
922 if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
925 for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
927 if (!font->font_maps[map_index])
929 // if a similar size has already been loaded, ignore this one
930 //abs(font->font_maps[map_index]->size - size) < 4
931 if (font->font_maps[map_index]->size == size)
935 if (map_index >= MAX_FONT_SIZES)
940 if (font->image_font)
941 fontface = (FT_Face)font->next->face;
943 fontface = (FT_Face)font->face;
944 return (Font_SearchSize(font, fontface, size) > 0);
947 Font_Postprocess(font, NULL, 0, 4, size*2, size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
949 memset(&temp, 0, sizeof(temp));
951 temp.glyphSize = size*2 + max(gpad_l + gpad_r, gpad_t + gpad_b);
952 if (!r_font_nonpoweroftwo.integer)
953 temp.glyphSize = CeilPowerOf2(temp.glyphSize);
954 temp.sfx = (1.0/64.0)/(double)size;
955 temp.sfy = (1.0/64.0)/(double)size;
956 temp.intSize = -1; // negative value: LoadMap must search now :)
957 if (!Font_LoadMap(font, &temp, 0, &fmap, NULL, false))
959 Con_Printf(CON_ERROR "ERROR: can't load the first character map for %s\n"
962 Font_UnloadFont(font);
965 font->font_maps[map_index] = temp.next;
967 fmap->sfx = temp.sfx;
968 fmap->sfy = temp.sfy;
970 // load the default kerning vector:
971 if (font->has_kerning)
975 fmap->kerning = (ft2_kerning_t *)Mem_Alloc(font_mempool, sizeof(ft2_kerning_t));
976 for (l = 0; l < 256; ++l)
978 for (r = 0; r < 256; ++r)
981 ul = qFT_Get_Char_Index((FT_Face)font->face, l);
982 ur = qFT_Get_Char_Index((FT_Face)font->face, r);
983 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
985 fmap->kerning->kerning[l][r][0] = 0;
986 fmap->kerning->kerning[l][r][1] = 0;
990 fmap->kerning->kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
991 fmap->kerning->kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
999 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
1002 float value = 1000000;
1004 int matchsize = -10000;
1006 float fsize_x, fsize_y;
1007 ft2_font_map_t **maps = font->font_maps;
1009 fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value;
1011 fsize_x = *outw * vid.width / vid_conwidth.value;
1013 fsize_y = *outh * vid.height / vid_conheight.value;
1018 fsize_x = fsize_y = 16;
1028 for (m = 0; m < MAX_FONT_SIZES; ++m)
1032 // "round up" to the bigger size if two equally-valued matches exist
1033 nval = 0.5 * (fabs(maps[m]->size - fsize_x) + fabs(maps[m]->size - fsize_y));
1034 if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
1038 matchsize = maps[m]->size;
1039 if (value == 0) // there is no better match
1043 if (value <= r_font_size_snapping.value)
1045 // do NOT keep the aspect for perfect rendering
1046 if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
1047 if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width;
1052 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
1054 if (index < 0 || index >= MAX_FONT_SIZES)
1056 return font->font_maps[index];
1059 static qbool Font_SetSize(ft2_font_t *font, float w, float h)
1061 if (font->currenth == h &&
1062 ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
1063 font->currentw == w)) // same size has been requested
1067 // sorry, but freetype doesn't seem to care about other sizes
1070 if (font->image_font)
1072 if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
1077 if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
1085 qbool Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
1087 ft2_font_map_t *fmap;
1088 if (!font->has_kerning || !r_font_kerning.integer)
1090 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1092 fmap = font->font_maps[map_index];
1095 if (left < 256 && right < 256)
1097 //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
1098 // quick-kerning, be aware of the size: scale it
1099 if (outx) *outx = fmap->kerning->kerning[left][right][0];// * (w / (float)fmap->size);
1100 if (outy) *outy = fmap->kerning->kerning[left][right][1];// * (h / (float)fmap->size);
1108 //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
1110 if (!Font_SetSize(font, w, h))
1112 // this deserves an error message
1113 Con_Printf("Failed to get kerning for %s\n", font->name);
1116 ul = qFT_Get_Char_Index(font->face, left);
1117 ur = qFT_Get_Char_Index(font->face, right);
1118 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
1120 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);
1121 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);
1125 if (!Font_SetSize(font, fmap->intSize, fmap->intSize))
1127 // this deserves an error message
1128 Con_Printf(CON_ERROR "Failed to get kerning for %s\n", font->name);
1131 ul = qFT_Get_Char_Index((FT_Face)font->face, left);
1132 ur = qFT_Get_Char_Index((FT_Face)font->face, right);
1133 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
1135 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);// * (w / (float)fmap->size);
1136 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);// * (h / (float)fmap->size);
1143 qbool Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
1145 return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
1148 // this is used to gracefully unload a map chain; the passed map
1149 // needs not necessarily be a startmap, so maps ahead of it can be kept
1150 static void UnloadMapChain(ft2_font_map_t *map)
1153 ft2_font_map_t *nextmap;
1154 // these may only be in a startmap
1155 if (map->kerning != NULL)
1156 Mem_Free(map->kerning);
1157 if (map->incmap != NULL)
1159 for (i = 0; i < FONT_CHARS_PER_LINE; ++i)
1160 if (map->incmap->data_tier1[i] != NULL)
1161 Mem_Free(map->incmap->data_tier1[i]);
1164 for (i = 0; i < FONT_CHAR_LINES; ++i)
1165 if (map->incmap->data_tier2[i] != NULL)
1166 Mem_Free(map->incmap->data_tier2[i]);
1169 Mem_Free(map->incmap);
1175 //Draw_FreePic(map->pic); // FIXME: refcounting needed...
1178 nextmap = map->next;
1184 void Font_UnloadFont(ft2_font_t *font)
1190 Font_UnloadFont(font->next);
1192 if (font->attachments && font->attachmentcount)
1194 for (i = 0; i < (int)font->attachmentcount; ++i) {
1195 if (font->attachments[i].data)
1196 fontfilecache_Free(font->attachments[i].data);
1198 Mem_Free(font->attachments);
1199 font->attachmentcount = 0;
1200 font->attachments = NULL;
1202 for (i = 0; i < MAX_FONT_SIZES; ++i)
1204 if (font->font_maps[i])
1206 UnloadMapChain(font->font_maps[i]);
1207 font->font_maps[i] = NULL;
1210 #ifndef DP_FREETYPE_STATIC
1213 if (!r_font_disable_freetype.integer)
1218 qFT_Done_Face((FT_Face)font->face);
1223 fontfilecache_Free(font->data);
1228 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size)
1230 float intSize = size;
1233 if (!Font_SetSize(font, intSize, intSize))
1235 Con_Printf(CON_ERROR "ERROR: can't set size for font %s: %f ((%f))\n", font->name, size, intSize);
1238 if ((fontface->size->metrics.height>>6) <= size)
1242 Con_Printf(CON_ERROR "ERROR: no appropriate size found for font %s: %f\n", font->name, size);
1249 // helper inline functions for incmap_post_process:
1251 static inline void update_pic_for_fontmap(ft2_font_map_t *fontmap, const char *identifier,
1252 int width, int height, unsigned char *data)
1254 fontmap->pic = Draw_NewPic(identifier, width, height, data, TEXTYPE_RGBA,
1255 TEXF_ALPHA | TEXF_CLAMP | (r_font_compress.integer > 0 ? TEXF_COMPRESS : 0));
1258 // glyphs' texture coords needs to be fixed when merging to bigger texture
1259 static inline void transform_glyph_coords(glyph_slot_t *glyph, float shiftx, float shifty, float scalex, float scaley)
1261 glyph->txmin = glyph->txmin * scalex + shiftx;
1262 glyph->txmax = glyph->txmax * scalex + shiftx;
1263 glyph->tymin = glyph->tymin * scaley + shifty;
1264 glyph->tymax = glyph->tymax * scaley + shifty;
1266 #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)
1267 #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)
1269 // pull glyph things from sourcemap to targetmap
1270 static inline void merge_single_map(ft2_font_map_t *targetmap, int targetindex, ft2_font_map_t *sourcemap, int sourceindex)
1272 targetmap->glyphs[targetindex] = sourcemap->glyphs[sourceindex];
1273 targetmap->glyphchars[targetindex] = sourcemap->glyphchars[sourceindex];
1276 #define calc_data_arguments(w, h) \
1277 width = startmap->glyphSize * w; \
1278 height = startmap->glyphSize * h; \
1279 pitch = width * bytes_per_pixel; \
1280 datasize = height * pitch;
1282 // do incremental map process
1283 static inline void incmap_post_process(font_incmap_t *incmap, Uchar ch,
1284 unsigned char *data, ft2_font_map_t **outmap, int *outmapch)
1286 #define bytes_per_pixel 4
1288 int index, targetmap_at;
1289 // where will the next `data` be placed
1290 int tier1_data_index, tier2_data_index;
1291 // metrics of data to manipulate
1292 int width, height, pitch, datasize;
1294 unsigned char *newdata, *chunk;
1295 ft2_font_map_t *startmap, *targetmap, *currentmap;
1296 #define M FONT_CHARS_PER_LINE
1297 #define N FONT_CHAR_LINES
1299 startmap = incmap->fontmap;
1300 index = incmap->charcount;
1301 tier1_data_index = index % M;
1302 tier2_data_index = incmap->tier1_merged;
1304 incmap->data_tier1[tier1_data_index] = data;
1306 if (index % M == M - 1)
1308 // tier 1 reduction, pieces to line
1309 calc_data_arguments(1, 1);
1310 targetmap_at = incmap->tier2_merged + incmap->tier1_merged;
1311 targetmap = startmap;
1312 for (i = 0; i < targetmap_at; ++i)
1313 targetmap = targetmap->next;
1314 currentmap = targetmap;
1315 newdata = (unsigned char *)Mem_Alloc(font_mempool, datasize * M);
1316 for (i = 0; i < M; ++i)
1318 chunk = incmap->data_tier1[i];
1321 for (y = 0; y < datasize; y += pitch)
1322 for (x = 0; x < pitch; ++x)
1323 newdata[y * M + i * pitch + x] = chunk[y + x];
1325 incmap->data_tier1[i] = NULL;
1326 merge_single_map(targetmap, i, currentmap, 0);
1327 fix_glyph_coords_tier1(&targetmap->glyphs[i], (float)i);
1328 currentmap = currentmap->next;
1330 update_pic_for_fontmap(targetmap, Draw_GetPicName(targetmap->pic), width * M, height, newdata);
1331 UnloadMapChain(targetmap->next);
1332 targetmap->next = NULL;
1333 incmap->data_tier2[tier2_data_index] = newdata;
1334 ++incmap->tier1_merged;
1335 incmap->tier1_merged %= M;
1336 incmap->newmap_start = INCMAP_START + targetmap_at + 1;
1337 // then give this merged map
1338 *outmap = targetmap;
1339 *outmapch = FONT_CHARS_PER_LINE - 1;
1341 if (index % (M * N) == M * N - 1)
1343 // tier 2 reduction, lines to full map
1344 calc_data_arguments(M, 1);
1345 targetmap_at = incmap->tier2_merged;
1346 targetmap = startmap;
1347 for (i = 0; i < targetmap_at; ++i)
1348 targetmap = targetmap->next;
1349 currentmap = targetmap;
1350 newdata = (unsigned char *)Mem_Alloc(font_mempool, datasize * N);
1351 for (i = 0; i < N; ++i)
1353 chunk = incmap->data_tier2[i];
1356 for (x = 0; x < datasize; ++x)
1357 newdata[i * datasize + x] = chunk[x];
1359 incmap->data_tier2[i] = NULL;
1360 for (j = 0; j < M; ++j)
1362 merge_single_map(targetmap, i * M + j, currentmap, j);
1363 fix_glyph_coords_tier2(&targetmap->glyphs[i * M + j], (float)i);
1365 currentmap = currentmap->next;
1367 update_pic_for_fontmap(targetmap, Draw_GetPicName(targetmap->pic), width, height * N, newdata);
1368 UnloadMapChain(targetmap->next);
1369 targetmap->next = NULL;
1371 ++incmap->tier2_merged;
1372 incmap->newmap_start = INCMAP_START + targetmap_at + 1;
1373 // then give this merged map
1374 *outmap = targetmap;
1375 *outmapch = FONT_CHARS_PER_MAP - 1;
1378 ++incmap->charcount;
1379 ++incmap->newmap_start;
1385 static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch,
1386 ft2_font_map_t **outmap, int *outmapch, qbool use_incmap)
1388 #define bytes_per_pixel 4
1390 char map_identifier[MAX_QPATH];
1391 unsigned long map_startglyph = _ch / FONT_CHARS_PER_MAP * FONT_CHARS_PER_MAP;
1392 unsigned char *data = NULL;
1393 FT_ULong ch = 0, mapch = 0;
1396 FT_Int32 load_flags;
1397 int gpad_l, gpad_r, gpad_t, gpad_b;
1400 int width, height, datasize;
1401 int glyph_row, glyph_column;
1403 int chars_per_line = FONT_CHARS_PER_LINE;
1404 int char_lines = FONT_CHAR_LINES;
1405 int chars_per_map = FONT_CHARS_PER_MAP;
1407 ft2_font_t *usefont;
1408 ft2_font_map_t *map, *next;
1409 font_incmap_t *incmap;
1413 incmap = mapstart->incmap;
1416 // only render one character in this map;
1417 // such small maps will be merged together later in `incmap_post_process`
1418 chars_per_line = char_lines = chars_per_map = 1;
1419 // and the index is incremental
1420 map_startglyph = incmap ? incmap->newmap_start : INCMAP_START;
1423 if (font->image_font)
1424 fontface = (FT_Face)font->next->face;
1426 fontface = (FT_Face)font->face;
1428 switch(font->settings->antialias)
1431 switch(font->settings->hinting)
1434 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1438 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1442 load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1448 switch(font->settings->hinting)
1451 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1454 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
1457 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1461 load_flags = FT_LOAD_TARGET_NORMAL;
1467 //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
1469 if (font->image_font && mapstart->intSize < 0)
1470 mapstart->intSize = mapstart->size;
1471 if (mapstart->intSize < 0)
1474 mapstart->intSize = mapstart->size;
1477 if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1479 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
1482 if ((fontface->size->metrics.height>>6) <= mapstart->size)
1484 if (mapstart->intSize < 2)
1486 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
1489 --mapstart->intSize;
1492 if ((mapstart->intSize = Font_SearchSize(font, fontface, mapstart->size)) <= 0)
1494 Con_DPrintf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
1497 if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1499 Con_Printf(CON_ERROR "ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
1503 map = (ft2_font_map_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
1506 Con_Printf(CON_ERROR "ERROR: Out of memory when allocating fontmap for %s\n", font->name);
1510 map->start = map_startglyph;
1512 // create a unique name for this map, then we will use it to make a unique cachepic_t to avoid redundant textures
1514 dpsnprintf(map_identifier, sizeof(map_identifier),
1515 "%s_cache_%g_%d_%g_%g_%g_%g_%g_%u_%lx",
1517 (double) mapstart->intSize,
1519 (double) font->settings->blur,
1520 (double) font->settings->outline,
1521 (double) font->settings->shadowx,
1522 (double) font->settings->shadowy,
1523 (double) font->settings->shadowz,
1524 (unsigned) map_startglyph,
1525 // add pointer as a unique part to avoid earlier incmaps' state being trashed
1526 use_incmap ? (unsigned long)mapstart : 0x0);
1529 * note 1: it appears that different font instances may have the same metrics, causing this pic being overwritten
1530 * will use startmap's pointer as a unique part to avoid earlier incmaps' dynamic pics being trashed
1531 * note 2: if this identifier is made too long, significient performance drop will take place
1532 * note 3: blur/outline/shadow are per-font settings, so a pointer to startmap & map size
1533 * already made these unique, hence they are omitted
1534 * note 4: font_diskcache is removed, this name can be less meaningful
1536 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%g_%p_%u",
1537 font->name, mapstart->intSize, mapstart, (unsigned) map_startglyph);
1539 // create a cachepic_t from the data now, or reuse an existing one
1540 if (developer_font.integer)
1541 Con_Printf("Generating font map %s (size: %.1f MB)\n", map_identifier, mapstart->glyphSize * (256 * 4 / 1048576.0) * mapstart->glyphSize);
1543 Font_Postprocess(font, NULL, 0, bytes_per_pixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
1545 // copy over the information
1546 map->size = mapstart->size;
1547 map->intSize = mapstart->intSize;
1548 map->glyphSize = mapstart->glyphSize;
1549 map->sfx = mapstart->sfx;
1550 map->sfy = mapstart->sfy;
1552 width = map->glyphSize * chars_per_line;
1553 height = map->glyphSize * char_lines;
1554 pitch = width * bytes_per_pixel;
1555 datasize = height * pitch;
1556 data = (unsigned char *)Mem_Alloc(font_mempool, datasize);
1559 Con_Printf(CON_ERROR "ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
1566 if (mapstart->incmap == NULL)
1569 incmap = mapstart->incmap = (font_incmap_t *)Mem_Alloc(font_mempool, sizeof(font_incmap_t));
1572 Con_Printf(CON_ERROR "ERROR: Out of memory when allocating incremental fontmap for %s\n", font->name);
1575 // this will be the startmap of incmap
1576 incmap->fontmap = map;
1577 incmap->newmap_start = INCMAP_START;
1581 // new maps for incmap shall always be the last one
1582 next = incmap->fontmap;
1583 while (next->next != NULL)
1590 // insert this normal map
1591 next = use_incmap ? incmap->fontmap : mapstart;
1592 while(next->next && next->next->start < map->start)
1594 map->next = next->next;
1598 // initialize as white texture with zero alpha
1600 while (tp < datasize)
1602 if (bytes_per_pixel == 4)
1613 ch = (FT_ULong)(use_incmap ? _ch : map->start);
1617 FT_ULong glyphIndex;
1621 unsigned char *imagedata = NULL, *dst, *src;
1622 glyph_slot_t *mapglyph;
1624 int pad_l, pad_r, pad_t, pad_b;
1626 if (developer_font.integer)
1627 Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
1629 map->glyphchars[mapch] = (Uchar)ch;
1633 imagedata = data + glyph_row * pitch * map->glyphSize + glyph_column * map->glyphSize * bytes_per_pixel;
1634 imagedata += gpad_t * pitch + gpad_l * bytes_per_pixel;
1636 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
1637 // we need the glyphIndex
1638 face = (FT_Face)font->face;
1640 if (font->image_font && mapch == ch && img_fontmap[mapch])
1642 map->glyphs[mapch].image = true;
1645 glyphIndex = qFT_Get_Char_Index(face, ch);
1646 if (glyphIndex == 0)
1648 // by convention, 0 is the "missing-glyph"-glyph
1649 // try to load from a fallback font
1650 for(usefont = font->next; usefont != NULL; usefont = usefont->next)
1652 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
1655 face = (FT_Face)usefont->face;
1656 glyphIndex = qFT_Get_Char_Index(face, ch);
1657 if (glyphIndex == 0)
1659 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1666 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1667 // now we let it use the "missing-glyph"-glyph
1668 face = (FT_Face)font->face;
1676 face = (FT_Face)font->face;
1677 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1680 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1681 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1686 glyph = face->glyph;
1687 bmp = &glyph->bitmap;
1692 if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
1693 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);
1694 if (w > map->glyphSize)
1695 w = map->glyphSize - gpad_l - gpad_r;
1696 if (h > map->glyphSize)
1702 switch (bmp->pixel_mode)
1704 case FT_PIXEL_MODE_MONO:
1705 if (developer_font.integer)
1706 Con_DPrint("glyphinfo: Pixel Mode: MONO\n");
1708 case FT_PIXEL_MODE_GRAY2:
1709 if (developer_font.integer)
1710 Con_DPrint("glyphinfo: Pixel Mode: GRAY2\n");
1712 case FT_PIXEL_MODE_GRAY4:
1713 if (developer_font.integer)
1714 Con_DPrint("glyphinfo: Pixel Mode: GRAY4\n");
1716 case FT_PIXEL_MODE_GRAY:
1717 if (developer_font.integer)
1718 Con_DPrint("glyphinfo: Pixel Mode: GRAY\n");
1721 if (developer_font.integer)
1722 Con_DPrintf("glyphinfo: Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1724 Con_Printf(CON_ERROR "ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1727 for (y = 0; y < h; ++y)
1729 dst = imagedata + y * pitch;
1730 src = bmp->buffer + y * bmp->pitch;
1732 switch (bmp->pixel_mode)
1734 case FT_PIXEL_MODE_MONO:
1735 dst += bytes_per_pixel - 1; // shift to alpha byte
1736 for (x = 0; x < bmp->width; x += 8)
1738 unsigned char c = *src++;
1739 *dst = 255 * !!((c & 0x80) >> 7); dst += bytes_per_pixel;
1740 *dst = 255 * !!((c & 0x40) >> 6); dst += bytes_per_pixel;
1741 *dst = 255 * !!((c & 0x20) >> 5); dst += bytes_per_pixel;
1742 *dst = 255 * !!((c & 0x10) >> 4); dst += bytes_per_pixel;
1743 *dst = 255 * !!((c & 0x08) >> 3); dst += bytes_per_pixel;
1744 *dst = 255 * !!((c & 0x04) >> 2); dst += bytes_per_pixel;
1745 *dst = 255 * !!((c & 0x02) >> 1); dst += bytes_per_pixel;
1746 *dst = 255 * !!((c & 0x01) >> 0); dst += bytes_per_pixel;
1749 case FT_PIXEL_MODE_GRAY2:
1750 dst += bytes_per_pixel - 1; // shift to alpha byte
1751 for (x = 0; x < bmp->width; x += 4)
1753 unsigned char c = *src++;
1754 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytes_per_pixel;
1755 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytes_per_pixel;
1756 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytes_per_pixel;
1757 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytes_per_pixel;
1760 case FT_PIXEL_MODE_GRAY4:
1761 dst += bytes_per_pixel - 1; // shift to alpha byte
1762 for (x = 0; x < bmp->width; x += 2)
1764 unsigned char c = *src++;
1765 *dst = ( ((c & 0xF0) >> 4) * 0x11); dst += bytes_per_pixel;
1766 *dst = ( ((c & 0x0F) ) * 0x11); dst += bytes_per_pixel;
1769 case FT_PIXEL_MODE_GRAY:
1770 // in this case pitch should equal width
1771 for (tp = 0; tp < bmp->pitch; ++tp)
1772 dst[(bytes_per_pixel - 1) + tp*bytes_per_pixel] = src[tp]; // copy the grey value into the alpha bytes
1774 //memcpy((void*)dst, (void*)src, bmp->pitch);
1775 //dst += bmp->pitch;
1786 Font_Postprocess(font, imagedata, pitch, bytes_per_pixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1794 Font_Postprocess(font, NULL, pitch, bytes_per_pixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1798 // now fill map->glyphs[ch - map->start]
1799 mapglyph = &map->glyphs[mapch];
1803 // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1805 double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1806 //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1807 double advance = (glyph->advance.x / 64.0) / map->size;
1808 //double mWidth = (glyph->metrics.width >> 6) / map->size;
1809 //double mHeight = (glyph->metrics.height >> 6) / map->size;
1811 mapglyph->txmin = ( (double)(glyph_column * map->glyphSize) + (double)(gpad_l - pad_l) ) / ( (double)(map->glyphSize * chars_per_line) );
1812 mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * chars_per_line) );
1813 mapglyph->tymin = ( (double)(glyph_row * map->glyphSize) + (double)(gpad_r - pad_r) ) / ( (double)(map->glyphSize * char_lines) );
1814 mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * char_lines) );
1815 //mapglyph->vxmin = bearingX;
1816 //mapglyph->vxmax = bearingX + mWidth;
1817 mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
1818 mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
1819 //mapglyph->vymin = -bearingY;
1820 //mapglyph->vymax = mHeight - bearingY;
1821 mapglyph->vymin = (-glyph->bitmap_top - pad_t) / map->size;
1822 mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
1823 //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);
1824 //mapglyph->advance_x = advance * usefont->size;
1825 //mapglyph->advance_x = advance;
1826 mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1827 mapglyph->advance_y = 0;
1829 if (developer_font.integer)
1831 Con_DPrintf("glyphinfo: Glyph: %lu at (%i, %i)\n", (unsigned long)ch, glyph_column, glyph_row);
1832 Con_DPrintf("glyphinfo: %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1833 if (ch >= 32 && ch <= 128)
1834 Con_DPrintf("glyphinfo: Character: %c\n", (int)ch);
1835 Con_DPrintf("glyphinfo: Vertex info:\n");
1836 Con_DPrintf("glyphinfo: X: ( %f -- %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1837 Con_DPrintf("glyphinfo: Y: ( %f -- %f )\n", mapglyph->vymin, mapglyph->vymax);
1838 Con_DPrintf("glyphinfo: Texture info:\n");
1839 Con_DPrintf("glyphinfo: S: ( %f -- %f )\n", mapglyph->txmin, mapglyph->txmax);
1840 Con_DPrintf("glyphinfo: T: ( %f -- %f )\n", mapglyph->tymin, mapglyph->tymax);
1841 Con_DPrintf("glyphinfo: Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1844 map->glyphs[mapch].image = false;
1847 if ((int)mapch == chars_per_map)
1849 if (++glyph_column % chars_per_line == 0)
1856 // update the pic returned by Draw_CachePic_Flags earlier to contain our texture
1857 update_pic_for_fontmap(map, map_identifier, width, height, data);
1859 if (!Draw_IsPicLoaded(map->pic))
1861 // if the first try isn't successful, keep it with a broken texture
1862 // otherwise we retry to load it every single frame where ft2 rendering is used
1863 // this would be bad...
1864 // only `data' must be freed
1865 Con_Printf(CON_ERROR "ERROR: Failed to generate texture for font %s size %f map %lu\n",
1866 font->name, mapstart->size, map_startglyph);
1874 // data will be kept in incmap for being merged later, freed afterward
1875 incmap_post_process(incmap, _ch, data, outmap, outmapch);
1881 if (outmapch != NULL)
1882 *outmapch = _ch - map->start;
1888 static qbool legacy_font_loading_api_alerted = false;
1889 static inline void alert_legacy_font_api(const char *name)
1891 if (!legacy_font_loading_api_alerted)
1893 Con_DPrintf(CON_WARN "Warning: You are using an legacy API '%s', which have certain limitations; please use 'Font_GetMapForChar' instead\n", name);
1894 legacy_font_loading_api_alerted = true;
1898 // legacy font API, please use `Font_GetMapForChar` instead
1899 qbool Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar ch, ft2_font_map_t **outmap)
1901 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1903 // the first map must have been loaded already
1904 if (!font->font_maps[map_index])
1906 alert_legacy_font_api("Font_LoadMapForIndex");
1907 return Font_LoadMap(font, font->font_maps[map_index], ch, outmap, NULL, false);
1910 // legacy font API. please use `Font_GetMapForChar` instead
1911 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1913 ft2_font_map_t *map = start;
1914 while (map && map->start + FONT_CHARS_PER_MAP <= ch)
1916 if (map && map->start > ch)
1918 alert_legacy_font_api("FontMap_FindForChar");
1922 static inline qbool should_use_incmap(Uchar ch)
1925 // optimize: a simple check logic for usual conditions
1926 if (ch < unicode_bigblocks[0])
1928 if (r_font_disable_incmaps.integer == 1)
1930 for (i = 0; i < (int)(sizeof(unicode_bigblocks) / sizeof(Uchar)); i += 2)
1931 if (unicode_bigblocks[i] <= ch && ch <= unicode_bigblocks[i + 1])
1936 static inline qbool get_char_from_incmap(ft2_font_map_t *map, Uchar ch, ft2_font_map_t **outmap, int *outmapch)
1939 font_incmap_t *incmap;
1941 incmap = map->incmap;
1946 map = incmap->fontmap;
1949 for (i = 0; i < FONT_CHARS_PER_MAP; ++i)
1951 if (map->glyphchars[i] == ch)
1957 else if (map->glyphchars[i] == 0)
1958 // this tier0/tier1 map ends here
1968 * Query for or load a font map for a character, with the character's place on it.
1969 * Supports the incremental map mechanism; returning if the operation is done successfully
1971 qbool Font_GetMapForChar(ft2_font_t *font, int map_index, Uchar ch, ft2_font_map_t **outmap, int *outmapch)
1974 ft2_font_map_t *map;
1977 map = Font_MapForIndex(font, map_index);
1979 // optimize: the first map must have been loaded already
1980 if (ch < FONT_CHARS_PER_MAP)
1987 // search for the character
1989 use_incmap = should_use_incmap(ch);
1994 *outmapch = ch % FONT_CHARS_PER_MAP;
1995 while (map && map->start + FONT_CHARS_PER_MAP <= ch)
1997 if (map && map->start <= ch)
2003 else if (get_char_from_incmap(map, ch, outmap, outmapch))
2007 // so no appropriate map was found, load one
2009 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
2011 return Font_LoadMap(font, font->font_maps[map_index], ch, outmap, outmapch, use_incmap);