]> git.xonotic.org Git - xonotic/darkplaces.git/blob - ft2.c
Merge PR 'sv_gameplayfix_stepmultipletimes 1: Prevent players moving too far/fast...
[xonotic/darkplaces.git] / ft2.c
1 /* FreeType 2 and UTF-8 encoding support for
2  * DarkPlaces
3  */
4 #include "quakedef.h"
5
6 #include "ft2.h"
7 #include "ft2_defs.h"
8 #include "ft2_fontdefs.h"
9 #include "image.h"
10
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
28 };
29
30 /*
31 ================================================================================
32 CVars introduced with the freetype extension
33 ================================================================================
34 */
35
36 cvar_t r_font_disable_freetype = {CF_CLIENT | CF_ARCHIVE, "r_font_disable_freetype", "0", "disable freetype support for fonts entirely"};
37 cvar_t r_font_use_alpha_textures = {CF_CLIENT | CF_ARCHIVE, "r_font_use_alpha_textures", "0", "use alpha-textures for font rendering, this should safe memory"};
38 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!"};
39 cvar_t r_font_kerning = {CF_CLIENT | CF_ARCHIVE, "r_font_kerning", "1", "Use kerning if available"};
40 cvar_t r_font_diskcache = {CF_CLIENT | CF_ARCHIVE, "r_font_diskcache", "0", "save font textures to disk for future loading rather than generating them every time"};
41 cvar_t r_font_compress = {CF_CLIENT | CF_ARCHIVE, "r_font_compress", "0", "use texture compression on font textures to save video memory"};
42 cvar_t r_font_nonpoweroftwo = {CF_CLIENT | CF_ARCHIVE, "r_font_nonpoweroftwo", "1", "use nonpoweroftwo textures for font (saves memory, potentially slower)"};
43 cvar_t developer_font = {CF_CLIENT | CF_ARCHIVE, "developer_font", "0", "prints debug messages about fonts"};
44
45 #ifndef DP_FREETYPE_STATIC
46
47 /*
48 ================================================================================
49 Function definitions. Taken from the freetype2 headers.
50 ================================================================================
51 */
52
53
54 FT_EXPORT( FT_Error )
55 (*qFT_Init_FreeType)( FT_Library  *alibrary );
56 FT_EXPORT( FT_Error )
57 (*qFT_Done_FreeType)( FT_Library  library );
58 /*
59 FT_EXPORT( FT_Error )
60 (*qFT_New_Face)( FT_Library   library,
61                  const char*  filepathname,
62                  FT_Long      face_index,
63                  FT_Face     *aface );
64 */
65 FT_EXPORT( FT_Error )
66 (*qFT_New_Memory_Face)( FT_Library      library,
67                         const FT_Byte*  file_base,
68                         FT_Long         file_size,
69                         FT_Long         face_index,
70                         FT_Face        *aface );
71 FT_EXPORT( FT_Error )
72 (*qFT_Done_Face)( FT_Face  face );
73 FT_EXPORT( FT_Error )
74 (*qFT_Select_Size)( FT_Face  face,
75                     FT_Int   strike_index );
76 FT_EXPORT( FT_Error )
77 (*qFT_Request_Size)( FT_Face          face,
78                      FT_Size_Request  req );
79 FT_EXPORT( FT_Error )
80 (*qFT_Set_Char_Size)( FT_Face     face,
81                       FT_F26Dot6  char_width,
82                       FT_F26Dot6  char_height,
83                       FT_UInt     horz_resolution,
84                       FT_UInt     vert_resolution );
85 FT_EXPORT( FT_Error )
86 (*qFT_Set_Pixel_Sizes)( FT_Face  face,
87                         FT_UInt  pixel_width,
88                         FT_UInt  pixel_height );
89 FT_EXPORT( FT_Error )
90 (*qFT_Load_Glyph)( FT_Face   face,
91                    FT_UInt   glyph_index,
92                    FT_Int32  load_flags );
93 FT_EXPORT( FT_Error )
94 (*qFT_Load_Char)( FT_Face   face,
95                   FT_ULong  char_code,
96                   FT_Int32  load_flags );
97 FT_EXPORT( FT_UInt )
98 (*qFT_Get_Char_Index)( FT_Face   face,
99                        FT_ULong  charcode );
100 FT_EXPORT( FT_Error )
101 (*qFT_Render_Glyph)( FT_GlyphSlot    slot,
102                      FT_Render_Mode  render_mode );
103 FT_EXPORT( FT_Error )
104 (*qFT_Get_Kerning)( FT_Face     face,
105                     FT_UInt     left_glyph,
106                     FT_UInt     right_glyph,
107                     FT_UInt     kern_mode,
108                     FT_Vector  *akerning );
109 FT_EXPORT( FT_Error )
110 (*qFT_Attach_Stream)( FT_Face        face,
111                       FT_Open_Args*  parameters );
112 /*
113 ================================================================================
114 Support for dynamically loading the FreeType2 library
115 ================================================================================
116 */
117
118 static dllfunction_t ft2funcs[] =
119 {
120         {"FT_Init_FreeType",            (void **) &qFT_Init_FreeType},
121         {"FT_Done_FreeType",            (void **) &qFT_Done_FreeType},
122         //{"FT_New_Face",                       (void **) &qFT_New_Face},
123         {"FT_New_Memory_Face",          (void **) &qFT_New_Memory_Face},
124         {"FT_Done_Face",                (void **) &qFT_Done_Face},
125         {"FT_Select_Size",              (void **) &qFT_Select_Size},
126         {"FT_Request_Size",             (void **) &qFT_Request_Size},
127         {"FT_Set_Char_Size",            (void **) &qFT_Set_Char_Size},
128         {"FT_Set_Pixel_Sizes",          (void **) &qFT_Set_Pixel_Sizes},
129         {"FT_Load_Glyph",               (void **) &qFT_Load_Glyph},
130         {"FT_Load_Char",                (void **) &qFT_Load_Char},
131         {"FT_Get_Char_Index",           (void **) &qFT_Get_Char_Index},
132         {"FT_Render_Glyph",             (void **) &qFT_Render_Glyph},
133         {"FT_Get_Kerning",              (void **) &qFT_Get_Kerning},
134         {"FT_Attach_Stream",            (void **) &qFT_Attach_Stream},
135         {NULL, NULL}
136 };
137
138 /// Handle for FreeType2 DLL
139 static dllhandle_t ft2_dll = NULL;
140
141 #else
142
143 FT_EXPORT( FT_Error )
144 (FT_Init_FreeType)( FT_Library  *alibrary );
145 FT_EXPORT( FT_Error )
146 (FT_Done_FreeType)( FT_Library  library );
147 /*
148 FT_EXPORT( FT_Error )
149 (FT_New_Face)( FT_Library   library,
150                  const char*  filepathname,
151                  FT_Long      face_index,
152                  FT_Face     *aface );
153 */
154 FT_EXPORT( FT_Error )
155 (FT_New_Memory_Face)( FT_Library      library,
156                         const FT_Byte*  file_base,
157                         FT_Long         file_size,
158                         FT_Long         face_index,
159                         FT_Face        *aface );
160 FT_EXPORT( FT_Error )
161 (FT_Done_Face)( FT_Face  face );
162 FT_EXPORT( FT_Error )
163 (FT_Select_Size)( FT_Face  face,
164                     FT_Int   strike_index );
165 FT_EXPORT( FT_Error )
166 (FT_Request_Size)( FT_Face          face,
167                      FT_Size_Request  req );
168 FT_EXPORT( FT_Error )
169 (FT_Set_Char_Size)( FT_Face     face,
170                       FT_F26Dot6  char_width,
171                       FT_F26Dot6  char_height,
172                       FT_UInt     horz_resolution,
173                       FT_UInt     vert_resolution );
174 FT_EXPORT( FT_Error )
175 (FT_Set_Pixel_Sizes)( FT_Face  face,
176                         FT_UInt  pixel_width,
177                         FT_UInt  pixel_height );
178 FT_EXPORT( FT_Error )
179 (FT_Load_Glyph)( FT_Face   face,
180                    FT_UInt   glyph_index,
181                    FT_Int32  load_flags );
182 FT_EXPORT( FT_Error )
183 (FT_Load_Char)( FT_Face   face,
184                   FT_ULong  char_code,
185                   FT_Int32  load_flags );
186 FT_EXPORT( FT_UInt )
187 (FT_Get_Char_Index)( FT_Face   face,
188                        FT_ULong  charcode );
189 FT_EXPORT( FT_Error )
190 (FT_Render_Glyph)( FT_GlyphSlot    slot,
191                      FT_Render_Mode  render_mode );
192 FT_EXPORT( FT_Error )
193 (FT_Get_Kerning)( FT_Face     face,
194                     FT_UInt     left_glyph,
195                     FT_UInt     right_glyph,
196                     FT_UInt     kern_mode,
197                     FT_Vector  *akerning );
198 FT_EXPORT( FT_Error )
199 (FT_Attach_Stream)( FT_Face        face,
200                       FT_Open_Args*  parameters );
201
202 #define qFT_Init_FreeType               FT_Init_FreeType
203 #define qFT_Done_FreeType               FT_Done_FreeType
204 //#define qFT_New_Face                  FT_New_Face
205 #define qFT_New_Memory_Face             FT_New_Memory_Face
206 #define qFT_Done_Face                   FT_Done_Face
207 #define qFT_Select_Size                 FT_Select_Size
208 #define qFT_Request_Size                FT_Request_Size
209 #define qFT_Set_Char_Size               FT_Set_Char_Size
210 #define qFT_Set_Pixel_Sizes             FT_Set_Pixel_Sizes
211 #define qFT_Load_Glyph                  FT_Load_Glyph
212 #define qFT_Load_Char                   FT_Load_Char
213 #define qFT_Get_Char_Index              FT_Get_Char_Index
214 #define qFT_Render_Glyph                FT_Render_Glyph
215 #define qFT_Get_Kerning                 FT_Get_Kerning
216 #define qFT_Attach_Stream               FT_Attach_Stream
217
218 #endif
219
220 /// Memory pool for fonts
221 static mempool_t *font_mempool= NULL;
222
223 /// FreeType library handle
224 static FT_Library font_ft2lib = NULL;
225
226 #define POSTPROCESS_MAXRADIUS 8
227 typedef struct
228 {
229         unsigned char *buf, *buf2;
230         int bufsize, bufwidth, bufheight, bufpitch;
231         float blur, outline, shadowx, shadowy, shadowz;
232         int padding_t, padding_b, padding_l, padding_r, blurpadding_lt, blurpadding_rb, outlinepadding_t, outlinepadding_b, outlinepadding_l, outlinepadding_r;
233         unsigned char circlematrix[2*POSTPROCESS_MAXRADIUS+1][2*POSTPROCESS_MAXRADIUS+1];
234         unsigned char gausstable[2*POSTPROCESS_MAXRADIUS+1];
235 }
236 font_postprocess_t;
237 static font_postprocess_t pp;
238
239 typedef struct fontfilecache_s
240 {
241         unsigned char *buf;
242         fs_offset_t len;
243         int refcount;
244         char path[MAX_QPATH];
245 }
246 fontfilecache_t;
247 #define MAX_FONTFILES 8
248 static fontfilecache_t fontfiles[MAX_FONTFILES];
249 static const unsigned char *fontfilecache_LoadFile(const char *path, qbool quiet, fs_offset_t *filesizepointer)
250 {
251         int i;
252         unsigned char *buf;
253
254         for(i = 0; i < MAX_FONTFILES; ++i)
255         {
256                 if(fontfiles[i].refcount > 0)
257                         if(!strcmp(path, fontfiles[i].path))
258                         {
259                                 *filesizepointer = fontfiles[i].len;
260                                 ++fontfiles[i].refcount;
261                                 return fontfiles[i].buf;
262                         }
263         }
264
265         buf = FS_LoadFile(path, font_mempool, quiet, filesizepointer);
266         if(buf)
267         {
268                 for(i = 0; i < MAX_FONTFILES; ++i)
269                         if(fontfiles[i].refcount <= 0)
270                         {
271                                 strlcpy(fontfiles[i].path, path, sizeof(fontfiles[i].path));
272                                 fontfiles[i].len = *filesizepointer;
273                                 fontfiles[i].buf = buf;
274                                 fontfiles[i].refcount = 1;
275                                 return buf;
276                         }
277         }
278
279         return buf;
280 }
281 static void fontfilecache_Free(const unsigned char *buf)
282 {
283         int i;
284         for(i = 0; i < MAX_FONTFILES; ++i)
285         {
286                 if(fontfiles[i].refcount > 0)
287                         if(fontfiles[i].buf == buf)
288                         {
289                                 if(--fontfiles[i].refcount <= 0)
290                                 {
291                                         Mem_Free(fontfiles[i].buf);
292                                         fontfiles[i].buf = NULL;
293                                 }
294                                 return;
295                         }
296         }
297         // if we get here, it used regular allocation
298         Mem_Free((void *) buf);
299 }
300 static void fontfilecache_FreeAll(void)
301 {
302         int i;
303         for(i = 0; i < MAX_FONTFILES; ++i)
304         {
305                 if(fontfiles[i].refcount > 0)
306                         Mem_Free(fontfiles[i].buf);
307                 fontfiles[i].buf = NULL;
308                 fontfiles[i].refcount = 0;
309         }
310 }
311
312 /*
313 ====================
314 Font_CloseLibrary
315
316 Unload the FreeType2 DLL
317 ====================
318 */
319 void Font_CloseLibrary (void)
320 {
321         fontfilecache_FreeAll();
322         if (font_mempool)
323                 Mem_FreePool(&font_mempool);
324         if (font_ft2lib && qFT_Done_FreeType)
325         {
326                 qFT_Done_FreeType(font_ft2lib);
327                 font_ft2lib = NULL;
328         }
329 #ifndef DP_FREETYPE_STATIC
330         Sys_FreeLibrary (&ft2_dll);
331 #endif
332         pp.buf = NULL;
333 }
334
335 /*
336 ====================
337 Font_OpenLibrary
338
339 Try to load the FreeType2 DLL
340 ====================
341 */
342 qbool Font_OpenLibrary (void)
343 {
344 #ifndef DP_FREETYPE_STATIC
345         const char* dllnames [] =
346         {
347 #if defined(WIN32)
348                 "libfreetype-6.dll",
349                 "freetype6.dll",
350 #elif defined(MACOSX)
351                 "libfreetype.6.dylib",
352                 "libfreetype.dylib",
353 #else
354                 "libfreetype.so.6",
355                 "libfreetype.so",
356 #endif
357                 NULL
358         };
359 #endif
360
361         if (r_font_disable_freetype.integer)
362                 return false;
363
364 #ifndef DP_FREETYPE_STATIC
365         // Already loaded?
366         if (ft2_dll)
367                 return true;
368
369         // Load the DLL
370         if (!Sys_LoadDependency (dllnames, &ft2_dll, ft2funcs))
371                 return false;
372 #endif
373         return true;
374 }
375
376 /*
377 ====================
378 Font_Init
379
380 Initialize the freetype2 font subsystem
381 ====================
382 */
383
384 void font_start(void)
385 {
386         if (!Font_OpenLibrary())
387                 return;
388
389         if (qFT_Init_FreeType(&font_ft2lib))
390         {
391                 Con_Print(CON_ERROR "ERROR: Failed to initialize the FreeType2 library!\n");
392                 Font_CloseLibrary();
393                 return;
394         }
395
396         font_mempool = Mem_AllocPool("FONT", 0, NULL);
397         if (!font_mempool)
398         {
399                 Con_Print(CON_ERROR "ERROR: Failed to allocate FONT memory pool!\n");
400                 Font_CloseLibrary();
401                 return;
402         }
403 }
404
405 void font_shutdown(void)
406 {
407         int i;
408         for (i = 0; i < dp_fonts.maxsize; ++i)
409         {
410                 if (dp_fonts.f[i].ft2)
411                 {
412                         Font_UnloadFont(dp_fonts.f[i].ft2);
413                         dp_fonts.f[i].ft2 = NULL;
414                 }
415         }
416         Font_CloseLibrary();
417 }
418
419 void font_newmap(void)
420 {
421 }
422
423 void Font_Init(void)
424 {
425         Cvar_RegisterVariable(&r_font_nonpoweroftwo);
426         Cvar_RegisterVariable(&r_font_disable_freetype);
427         Cvar_RegisterVariable(&r_font_use_alpha_textures);
428         Cvar_RegisterVariable(&r_font_size_snapping);
429         Cvar_RegisterVariable(&r_font_kerning);
430         Cvar_RegisterVariable(&r_font_diskcache);
431         Cvar_RegisterVariable(&r_font_compress);
432         Cvar_RegisterVariable(&developer_font);
433
434         // let's open it at startup already
435         Font_OpenLibrary();
436 }
437
438 /*
439 ================================================================================
440 Implementation of a more or less lazy font loading and rendering code.
441 ================================================================================
442 */
443
444 #include "ft2_fontdefs.h"
445
446 ft2_font_t *Font_Alloc(void)
447 {
448 #ifndef DP_FREETYPE_STATIC
449         if (!ft2_dll)
450 #else
451         if (r_font_disable_freetype.integer)
452 #endif
453                 return NULL;
454         return (ft2_font_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_t));
455 }
456
457 static qbool Font_Attach(ft2_font_t *font, ft2_attachment_t *attachment)
458 {
459         ft2_attachment_t *na;
460
461         font->attachmentcount++;
462         na = (ft2_attachment_t*)Mem_Alloc(font_mempool, sizeof(font->attachments[0]) * font->attachmentcount);
463         if (na == NULL)
464                 return false;
465         if (font->attachments && font->attachmentcount > 1)
466         {
467                 memcpy(na, font->attachments, sizeof(font->attachments[0]) * (font->attachmentcount - 1));
468                 Mem_Free(font->attachments);
469         }
470         memcpy(na + sizeof(font->attachments[0]) * (font->attachmentcount - 1), attachment, sizeof(*attachment));
471         font->attachments = na;
472         return true;
473 }
474
475 float Font_VirtualToRealSize(float sz)
476 {
477         int vh;
478         //int vw;
479         int si;
480         float sn;
481         if(sz < 0)
482                 return sz;
483         //vw = ((vid.width > 0) ? vid.width : vid_width.value);
484         vh = ((vid.height > 0) ? vid.height : vid_height.value);
485         // now try to scale to our actual size:
486         sn = sz * vh / vid_conheight.value;
487         si = (int)sn;
488         if ( sn - (float)si >= 0.5 )
489                 ++si;
490         return si;
491 }
492
493 float Font_SnapTo(float val, float snapwidth)
494 {
495         return floor(val / snapwidth + 0.5f) * snapwidth;
496 }
497
498 static qbool Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font);
499 static qbool Font_LoadSize(ft2_font_t *font, float size, qbool check_only);
500 qbool Font_LoadFont(const char *name, dp_font_t *dpfnt)
501 {
502         int s, count, i;
503         ft2_font_t *ft2, *fbfont, *fb;
504         char vabuf[1024];
505
506         ft2 = Font_Alloc();
507         if (!ft2)
508         {
509                 dpfnt->ft2 = NULL;
510                 return false;
511         }
512
513         // check if a fallback font has been specified, if it has been, and the
514         // font fails to load, use the image font as main font
515         for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
516         {
517                 if (dpfnt->fallbacks[i][0])
518                         break;
519         }
520
521         if (!Font_LoadFile(name, dpfnt->req_face, &dpfnt->settings, ft2))
522         {
523                 if (i >= MAX_FONT_FALLBACKS)
524                 {
525                         dpfnt->ft2 = NULL;
526                         Mem_Free(ft2);
527                         return false;
528                 }
529                 strlcpy(ft2->name, name, sizeof(ft2->name));
530                 ft2->image_font = true;
531                 ft2->has_kerning = false;
532         }
533         else
534         {
535                 ft2->image_font = false;
536         }
537
538         // attempt to load fallback fonts:
539         fbfont = ft2;
540         for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
541         {
542                 if (!dpfnt->fallbacks[i][0])
543                         break;
544                 if (! (fb = Font_Alloc()) )
545                 {
546                         Con_Printf(CON_ERROR "Failed to allocate font for fallback %i of font %s\n", i, name);
547                         break;
548                 }
549
550                 if (!Font_LoadFile(dpfnt->fallbacks[i], dpfnt->fallback_faces[i], &dpfnt->settings, fb))
551                 {
552                         if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.tga", dpfnt->fallbacks[i])))
553                         if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.png", dpfnt->fallbacks[i])))
554                         if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.jpg", dpfnt->fallbacks[i])))
555                         if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.pcx", dpfnt->fallbacks[i])))
556                                 Con_Printf(CON_ERROR "Failed to load font %s for fallback %i of font %s\n", dpfnt->fallbacks[i], i, name);
557                         Mem_Free(fb);
558                         continue;
559                 }
560                 count = 0;
561                 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
562                 {
563                         if (Font_LoadSize(fb, Font_VirtualToRealSize(dpfnt->req_sizes[s]), true))
564                                 ++count;
565                 }
566                 if (!count)
567                 {
568                         Con_Printf(CON_ERROR "Failed to allocate font for fallback %i of font %s\n", i, name);
569                         Font_UnloadFont(fb);
570                         Mem_Free(fb);
571                         break;
572                 }
573                 // at least one size of the fallback font loaded successfully
574                 // link it:
575                 fbfont->next = fb;
576                 fbfont = fb;
577         }
578
579         if (fbfont == ft2 && ft2->image_font)
580         {
581                 // no fallbacks were loaded successfully:
582                 dpfnt->ft2 = NULL;
583                 Mem_Free(ft2);
584                 return false;
585         }
586
587         count = 0;
588         for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
589         {
590                 if (Font_LoadSize(ft2, Font_VirtualToRealSize(dpfnt->req_sizes[s]), false))
591                         ++count;
592         }
593         if (!count)
594         {
595                 // loading failed for every requested size
596                 Font_UnloadFont(ft2);
597                 Mem_Free(ft2);
598                 dpfnt->ft2 = NULL;
599                 return false;
600         }
601
602         //Con_Printf("%i sizes loaded\n", count);
603         dpfnt->ft2 = ft2;
604         return true;
605 }
606
607 static qbool Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font)
608 {
609         size_t namelen;
610         char filename[MAX_QPATH];
611         int status;
612         size_t i;
613         const unsigned char *data;
614         fs_offset_t datasize;
615
616         memset(font, 0, sizeof(*font));
617
618         if (!Font_OpenLibrary())
619         {
620                 if (!r_font_disable_freetype.integer)
621                 {
622                         Con_Printf(CON_WARN "WARNING: can't open load font %s\n"
623                                    "You need the FreeType2 DLL to load font files\n",
624                                    name);
625                 }
626                 return false;
627         }
628
629         font->settings = settings;
630
631         namelen = strlen(name);
632         if (namelen + 5 > sizeof(filename))
633         {
634                 Con_Printf(CON_WARN "WARNING: too long font name. Cannot load this.\n");
635                 return false;
636         }
637
638         // try load direct file
639         memcpy(filename, name, namelen+1);
640         data = fontfilecache_LoadFile(filename, false, &datasize);
641         // try load .ttf
642         if (!data)
643         {
644                 memcpy(filename + namelen, ".ttf", 5);
645                 data = fontfilecache_LoadFile(filename, false, &datasize);
646         }
647         // try load .otf
648         if (!data)
649         {
650                 memcpy(filename + namelen, ".otf", 5);
651                 data = fontfilecache_LoadFile(filename, false, &datasize);
652         }
653         // try load .pfb/afm
654         if (!data)
655         {
656                 ft2_attachment_t afm;
657
658                 memcpy(filename + namelen, ".pfb", 5);
659                 data = fontfilecache_LoadFile(filename, false, &datasize);
660
661                 if (data)
662                 {
663                         memcpy(filename + namelen, ".afm", 5);
664                         afm.data = fontfilecache_LoadFile(filename, false, &afm.size);
665
666                         if (afm.data)
667                                 Font_Attach(font, &afm);
668                 }
669         }
670         if (!data)
671         {
672                 // FS_LoadFile being not-quiet should print an error :)
673                 return false;
674         }
675         Con_DPrintf("Loading font %s face %i...\n", filename, _face);
676
677         status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
678         if (status && _face != 0)
679         {
680                 Con_Printf(CON_ERROR "Failed to load face %i of %s. Falling back to face 0\n", _face, name);
681                 _face = 0;
682                 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
683         }
684         font->data = data;
685         if (status)
686         {
687                 Con_Printf(CON_ERROR "ERROR: can't create face for %s\n"
688                            "Error %i\n", // TODO: error strings
689                            name, status);
690                 Font_UnloadFont(font);
691                 return false;
692         }
693
694         // add the attachments
695         for (i = 0; i < font->attachmentcount; ++i)
696         {
697                 FT_Open_Args args;
698                 memset(&args, 0, sizeof(args));
699                 args.flags = FT_OPEN_MEMORY;
700                 args.memory_base = (const FT_Byte*)font->attachments[i].data;
701                 args.memory_size = font->attachments[i].size;
702                 if (qFT_Attach_Stream((FT_Face)font->face, &args))
703                         Con_Printf(CON_ERROR "Failed to add attachment %u to %s\n", (unsigned)i, font->name);
704         }
705
706         strlcpy(font->name, name, sizeof(font->name));
707         font->image_font = false;
708         font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
709         return true;
710 }
711
712 static void Font_Postprocess_Update(ft2_font_t *fnt, int bpp, int w, int h)
713 {
714         int needed, x, y;
715         float gausstable[2*POSTPROCESS_MAXRADIUS+1];
716         qbool need_gauss  = (!pp.buf || pp.blur != fnt->settings->blur || pp.shadowz != fnt->settings->shadowz);
717         qbool need_circle = (!pp.buf || pp.outline != fnt->settings->outline || pp.shadowx != fnt->settings->shadowx || pp.shadowy != fnt->settings->shadowy);
718         pp.blur = fnt->settings->blur;
719         pp.outline = fnt->settings->outline;
720         pp.shadowx = fnt->settings->shadowx;
721         pp.shadowy = fnt->settings->shadowy;
722         pp.shadowz = fnt->settings->shadowz;
723         pp.outlinepadding_l = bound(0, ceil(pp.outline - pp.shadowx), POSTPROCESS_MAXRADIUS);
724         pp.outlinepadding_r = bound(0, ceil(pp.outline + pp.shadowx), POSTPROCESS_MAXRADIUS);
725         pp.outlinepadding_t = bound(0, ceil(pp.outline - pp.shadowy), POSTPROCESS_MAXRADIUS);
726         pp.outlinepadding_b = bound(0, ceil(pp.outline + pp.shadowy), POSTPROCESS_MAXRADIUS);
727         pp.blurpadding_lt = bound(0, ceil(pp.blur - pp.shadowz), POSTPROCESS_MAXRADIUS);
728         pp.blurpadding_rb = bound(0, ceil(pp.blur + pp.shadowz), POSTPROCESS_MAXRADIUS);
729         pp.padding_l = pp.blurpadding_lt + pp.outlinepadding_l;
730         pp.padding_r = pp.blurpadding_rb + pp.outlinepadding_r;
731         pp.padding_t = pp.blurpadding_lt + pp.outlinepadding_t;
732         pp.padding_b = pp.blurpadding_rb + pp.outlinepadding_b;
733         if(need_gauss)
734         {
735                 float sum = 0;
736                 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
737                         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));
738                 for(x = -pp.blurpadding_rb; x <= pp.blurpadding_lt; ++x)
739                         sum += gausstable[POSTPROCESS_MAXRADIUS+x];
740                 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
741                         pp.gausstable[POSTPROCESS_MAXRADIUS+x] = floor(gausstable[POSTPROCESS_MAXRADIUS+x] / sum * 255 + 0.5);
742         }
743         if(need_circle)
744         {
745                 for(y = -POSTPROCESS_MAXRADIUS; y <= POSTPROCESS_MAXRADIUS; ++y)
746                         for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
747                         {
748                                 float d = pp.outline + 1 - sqrt(pow(x + pp.shadowx, 2) + pow(y + pp.shadowy, 2));
749                                 pp.circlematrix[POSTPROCESS_MAXRADIUS+y][POSTPROCESS_MAXRADIUS+x] = (d >= 1) ? 255 : (d <= 0) ? 0 : floor(d * 255 + 0.5);
750                         }
751         }
752         pp.bufwidth = w + pp.padding_l + pp.padding_r;
753         pp.bufheight = h + pp.padding_t + pp.padding_b;
754         pp.bufpitch = pp.bufwidth;
755         needed = pp.bufwidth * pp.bufheight;
756         if(!pp.buf || pp.bufsize < needed * 2)
757         {
758                 if(pp.buf)
759                         Mem_Free(pp.buf);
760                 pp.bufsize = needed * 4;
761                 pp.buf = (unsigned char *)Mem_Alloc(font_mempool, pp.bufsize);
762                 pp.buf2 = pp.buf + needed;
763         }
764 }
765
766 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)
767 {
768         int x, y;
769
770         // calculate gauss table
771         Font_Postprocess_Update(fnt, bpp, w, h);
772
773         if(imagedata)
774         {
775                 // enlarge buffer
776                 // perform operation, not exceeding the passed padding values,
777                 // but possibly reducing them
778                 *pad_l = min(*pad_l, pp.padding_l);
779                 *pad_r = min(*pad_r, pp.padding_r);
780                 *pad_t = min(*pad_t, pp.padding_t);
781                 *pad_b = min(*pad_b, pp.padding_b);
782
783                 // outline the font (RGBA only)
784                 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
785                 {
786                         // this is like mplayer subtitle rendering
787                         // bbuffer, bitmap buffer: this is our font
788                         // abuffer, alpha buffer: this is pp.buf
789                         // tmp: this is pp.buf2
790
791                         // create outline buffer
792                         memset(pp.buf, 0, pp.bufwidth * pp.bufheight);
793                         for(y = -*pad_t; y < h + *pad_b; ++y)
794                                 for(x = -*pad_l; x < w + *pad_r; ++x)
795                                 {
796                                         int x1 = max(-x, -pp.outlinepadding_r);
797                                         int y1 = max(-y, -pp.outlinepadding_b);
798                                         int x2 = min(pp.outlinepadding_l, w-1-x);
799                                         int y2 = min(pp.outlinepadding_t, h-1-y);
800                                         int mx, my;
801                                         int cur = 0;
802                                         int highest = 0;
803                                         for(my = y1; my <= y2; ++my)
804                                                 for(mx = x1; mx <= x2; ++mx)
805                                                 {
806                                                         cur = pp.circlematrix[POSTPROCESS_MAXRADIUS+my][POSTPROCESS_MAXRADIUS+mx] * (int)imagedata[(x+mx) * bpp + pitch * (y+my) + (bpp - 1)];
807                                                         if(cur > highest)
808                                                                 highest = cur;
809                                                 }
810                                         pp.buf[((x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t))] = (highest + 128) / 255;
811                                 }
812
813                         // blur the outline buffer
814                         if(pp.blur > 0 || pp.shadowz != 0)
815                         {
816                                 // horizontal blur
817                                 for(y = 0; y < pp.bufheight; ++y)
818                                         for(x = 0; x < pp.bufwidth; ++x)
819                                         {
820                                                 int x1 = max(-x, -pp.blurpadding_rb);
821                                                 int x2 = min(pp.blurpadding_lt, pp.bufwidth-1-x);
822                                                 int mx;
823                                                 int blurred = 0;
824                                                 for(mx = x1; mx <= x2; ++mx)
825                                                         blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+mx] * (int)pp.buf[(x+mx) + pp.bufpitch * y];
826                                                 pp.buf2[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
827                                         }
828
829                                 // vertical blur
830                                 for(y = 0; y < pp.bufheight; ++y)
831                                         for(x = 0; x < pp.bufwidth; ++x)
832                                         {
833                                                 int y1 = max(-y, -pp.blurpadding_rb);
834                                                 int y2 = min(pp.blurpadding_lt, pp.bufheight-1-y);
835                                                 int my;
836                                                 int blurred = 0;
837                                                 for(my = y1; my <= y2; ++my)
838                                                         blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+my] * (int)pp.buf2[x + pp.bufpitch * (y+my)];
839                                                 pp.buf[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
840                                         }
841                         }
842
843                         // paste the outline below the font
844                         for(y = -*pad_t; y < h + *pad_b; ++y)
845                                 for(x = -*pad_l; x < w + *pad_r; ++x)
846                                 {
847                                         unsigned char outlinealpha = pp.buf[(x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t)];
848                                         if(outlinealpha > 0)
849                                         {
850                                                 unsigned char oldalpha = imagedata[x * bpp + pitch * y + (bpp - 1)];
851                                                 // a' = 1 - (1 - a1) (1 - a2)
852                                                 unsigned char newalpha = 255 - ((255 - (int)outlinealpha) * (255 - (int)oldalpha)) / 255; // this is >= oldalpha
853                                                 // c' = (a2 c2 - a1 a2 c1 + a1 c1) / a' = (a2 c2 + a1 (1 - a2) c1) / a'
854                                                 unsigned char oldfactor     = (255 * (int)oldalpha) / newalpha;
855                                                 //unsigned char outlinefactor = ((255 - oldalpha) * (int)outlinealpha) / newalpha;
856                                                 int i;
857                                                 for(i = 0; i < bpp-1; ++i)
858                                                 {
859                                                         unsigned char c = imagedata[x * bpp + pitch * y + i];
860                                                         c = (c * (int)oldfactor) / 255 /* + outlinecolor[i] * (int)outlinefactor */;
861                                                         imagedata[x * bpp + pitch * y + i] = c;
862                                                 }
863                                                 imagedata[x * bpp + pitch * y + (bpp - 1)] = newalpha;
864                                         }
865                                         //imagedata[x * bpp + pitch * y + (bpp - 1)] |= 0x80;
866                                 }
867                 }
868         }
869         else if(pitch)
870         {
871                 // perform operation, not exceeding the passed padding values,
872                 // but possibly reducing them
873                 *pad_l = min(*pad_l, pp.padding_l);
874                 *pad_r = min(*pad_r, pp.padding_r);
875                 *pad_t = min(*pad_t, pp.padding_t);
876                 *pad_b = min(*pad_b, pp.padding_b);
877         }
878         else
879         {
880                 // just calculate parameters
881                 *pad_l = pp.padding_l;
882                 *pad_r = pp.padding_r;
883                 *pad_t = pp.padding_t;
884                 *pad_b = pp.padding_b;
885         }
886 }
887
888 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size);
889 static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
890 static qbool Font_LoadSize(ft2_font_t *font, float size, qbool check_only)
891 {
892         int map_index;
893         ft2_font_map_t *fmap, temp;
894         int gpad_l, gpad_r, gpad_t, gpad_b;
895
896         if (!(size > 0.001f && size < 1000.0f))
897                 size = 0;
898
899         if (!size)
900                 size = 16;
901         if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
902                 return false;
903
904         for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
905         {
906                 if (!font->font_maps[map_index])
907                         break;
908                 // if a similar size has already been loaded, ignore this one
909                 //abs(font->font_maps[map_index]->size - size) < 4
910                 if (font->font_maps[map_index]->size == size)
911                         return true;
912         }
913
914         if (map_index >= MAX_FONT_SIZES)
915                 return false;
916
917         if (check_only) {
918                 FT_Face fontface;
919                 if (font->image_font)
920                         fontface = (FT_Face)font->next->face;
921                 else
922                         fontface = (FT_Face)font->face;
923                 return (Font_SearchSize(font, fontface, size) > 0);
924         }
925
926         Font_Postprocess(font, NULL, 0, 4, size*2, size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
927
928         memset(&temp, 0, sizeof(temp));
929         temp.size = size;
930         temp.glyphSize = size*2 + max(gpad_l + gpad_r, gpad_t + gpad_b);
931         if (!r_font_nonpoweroftwo.integer)
932                 temp.glyphSize = CeilPowerOf2(temp.glyphSize);
933         temp.sfx = (1.0/64.0)/(double)size;
934         temp.sfy = (1.0/64.0)/(double)size;
935         temp.intSize = -1; // negative value: LoadMap must search now :)
936         if (!Font_LoadMap(font, &temp, 0, &fmap))
937         {
938                 Con_Printf(CON_ERROR "ERROR: can't load the first character map for %s\n"
939                            "This is fatal\n",
940                            font->name);
941                 Font_UnloadFont(font);
942                 return false;
943         }
944         font->font_maps[map_index] = temp.next;
945
946         fmap->sfx = temp.sfx;
947         fmap->sfy = temp.sfy;
948
949         // load the default kerning vector:
950         if (font->has_kerning)
951         {
952                 Uchar l, r;
953                 FT_Vector kernvec;
954                 for (l = 0; l < 256; ++l)
955                 {
956                         for (r = 0; r < 256; ++r)
957                         {
958                                 FT_ULong ul, ur;
959                                 ul = qFT_Get_Char_Index((FT_Face)font->face, l);
960                                 ur = qFT_Get_Char_Index((FT_Face)font->face, r);
961                                 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
962                                 {
963                                         fmap->kerning.kerning[l][r][0] = 0;
964                                         fmap->kerning.kerning[l][r][1] = 0;
965                                 }
966                                 else
967                                 {
968                                         fmap->kerning.kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
969                                         fmap->kerning.kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
970                                 }
971                         }
972                 }
973         }
974         return true;
975 }
976
977 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
978 {
979         int match = -1;
980         float value = 1000000;
981         float nval;
982         int matchsize = -10000;
983         int m;
984         float fsize_x, fsize_y;
985         ft2_font_map_t **maps = font->font_maps;
986
987         fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value;
988         if(outw && *outw)
989                 fsize_x = *outw * vid.width / vid_conwidth.value;
990         if(outh && *outh)
991                 fsize_y = *outh * vid.height / vid_conheight.value;
992
993         if (fsize_x < 0)
994         {
995                 if(fsize_y < 0)
996                         fsize_x = fsize_y = 16;
997                 else
998                         fsize_x = fsize_y;
999         }
1000         else
1001         {
1002                 if(fsize_y < 0)
1003                         fsize_y = fsize_x;
1004         }
1005
1006         for (m = 0; m < MAX_FONT_SIZES; ++m)
1007         {
1008                 if (!maps[m])
1009                         continue;
1010                 // "round up" to the bigger size if two equally-valued matches exist
1011                 nval = 0.5 * (fabs(maps[m]->size - fsize_x) + fabs(maps[m]->size - fsize_y));
1012                 if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
1013                 {
1014                         value = nval;
1015                         match = m;
1016                         matchsize = maps[m]->size;
1017                         if (value == 0) // there is no better match
1018                                 break;
1019                 }
1020         }
1021         if (value <= r_font_size_snapping.value)
1022         {
1023                 // do NOT keep the aspect for perfect rendering
1024                 if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
1025                 if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width;
1026         }
1027         return match;
1028 }
1029
1030 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
1031 {
1032         if (index < 0 || index >= MAX_FONT_SIZES)
1033                 return NULL;
1034         return font->font_maps[index];
1035 }
1036
1037 static qbool Font_SetSize(ft2_font_t *font, float w, float h)
1038 {
1039         if (font->currenth == h &&
1040             ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
1041              font->currentw == w)) // same size has been requested
1042         {
1043                 return true;
1044         }
1045         // sorry, but freetype doesn't seem to care about other sizes
1046         w = (int)w;
1047         h = (int)h;
1048         if (font->image_font)
1049         {
1050                 if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
1051                         return false;
1052         }
1053         else
1054         {
1055                 if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
1056                         return false;
1057         }
1058         font->currentw = w;
1059         font->currenth = h;
1060         return true;
1061 }
1062
1063 qbool Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
1064 {
1065         ft2_font_map_t *fmap;
1066         if (!font->has_kerning || !r_font_kerning.integer)
1067                 return false;
1068         if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1069                 return false;
1070         fmap = font->font_maps[map_index];
1071         if (!fmap)
1072                 return false;
1073         if (left < 256 && right < 256)
1074         {
1075                 //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
1076                 // quick-kerning, be aware of the size: scale it
1077                 if (outx) *outx = fmap->kerning.kerning[left][right][0];// * (w / (float)fmap->size);
1078                 if (outy) *outy = fmap->kerning.kerning[left][right][1];// * (h / (float)fmap->size);
1079                 return true;
1080         }
1081         else
1082         {
1083                 FT_Vector kernvec;
1084                 FT_ULong ul, ur;
1085
1086                 //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
1087 #if 0
1088                 if (!Font_SetSize(font, w, h))
1089                 {
1090                         // this deserves an error message
1091                         Con_Printf("Failed to get kerning for %s\n", font->name);
1092                         return false;
1093                 }
1094                 ul = qFT_Get_Char_Index(font->face, left);
1095                 ur = qFT_Get_Char_Index(font->face, right);
1096                 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
1097                 {
1098                         if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);
1099                         if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);
1100                         return true;
1101                 }
1102 #endif
1103                 if (!Font_SetSize(font, fmap->intSize, fmap->intSize))
1104                 {
1105                         // this deserves an error message
1106                         Con_Printf(CON_ERROR "Failed to get kerning for %s\n", font->name);
1107                         return false;
1108                 }
1109                 ul = qFT_Get_Char_Index((FT_Face)font->face, left);
1110                 ur = qFT_Get_Char_Index((FT_Face)font->face, right);
1111                 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
1112                 {
1113                         if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);// * (w / (float)fmap->size);
1114                         if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);// * (h / (float)fmap->size);
1115                         return true;
1116                 }
1117                 return false;
1118         }
1119 }
1120
1121 qbool Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
1122 {
1123         return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
1124 }
1125
1126 static void UnloadMapRec(ft2_font_map_t *map)
1127 {
1128         if (map->pic)
1129         {
1130                 //Draw_FreePic(map->pic); // FIXME: refcounting needed...
1131                 map->pic = NULL;
1132         }
1133         if (map->next)
1134                 UnloadMapRec(map->next);
1135         Mem_Free(map);
1136 }
1137
1138 void Font_UnloadFont(ft2_font_t *font)
1139 {
1140         int i;
1141
1142         // unload fallbacks
1143         if(font->next)
1144                 Font_UnloadFont(font->next);
1145
1146         if (font->attachments && font->attachmentcount)
1147         {
1148                 for (i = 0; i < (int)font->attachmentcount; ++i) {
1149                         if (font->attachments[i].data)
1150                                 fontfilecache_Free(font->attachments[i].data);
1151                 }
1152                 Mem_Free(font->attachments);
1153                 font->attachmentcount = 0;
1154                 font->attachments = NULL;
1155         }
1156         for (i = 0; i < MAX_FONT_SIZES; ++i)
1157         {
1158                 if (font->font_maps[i])
1159                 {
1160                         UnloadMapRec(font->font_maps[i]);
1161                         font->font_maps[i] = NULL;
1162                 }
1163         }
1164 #ifndef DP_FREETYPE_STATIC
1165         if (ft2_dll)
1166 #else
1167         if (!r_font_disable_freetype.integer)
1168 #endif
1169         {
1170                 if (font->face)
1171                 {
1172                         qFT_Done_Face((FT_Face)font->face);
1173                         font->face = NULL;
1174                 }
1175         }
1176         if (font->data) {
1177             fontfilecache_Free(font->data);
1178             font->data = NULL;
1179         }
1180 }
1181
1182 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size)
1183 {
1184         float intSize = size;
1185         while (1)
1186         {
1187                 if (!Font_SetSize(font, intSize, intSize))
1188                 {
1189                         Con_Printf(CON_ERROR "ERROR: can't set size for font %s: %f ((%f))\n", font->name, size, intSize);
1190                         return -1;
1191                 }
1192                 if ((fontface->size->metrics.height>>6) <= size)
1193                         return intSize;
1194                 if (intSize < 2)
1195                 {
1196                         Con_Printf(CON_ERROR "ERROR: no appropriate size found for font %s: %f\n", font->name, size);
1197                         return -1;
1198                 }
1199                 --intSize;
1200         }
1201 }
1202
1203 static qbool Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
1204 {
1205         char map_identifier[MAX_QPATH];
1206         unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
1207         unsigned char *data = NULL;
1208         FT_ULong ch, mapch;
1209         int status;
1210         int tp;
1211         FT_Int32 load_flags;
1212         int gpad_l, gpad_r, gpad_t, gpad_b;
1213         char vabuf[1024];
1214
1215         int pitch;
1216         int gR, gC; // glyph position: row and column
1217
1218         ft2_font_map_t *map, *next;
1219         ft2_font_t *usefont;
1220
1221         FT_Face fontface;
1222
1223         int bytesPerPixel = 4; // change the conversion loop too if you change this!
1224
1225         if (outmap)
1226                 *outmap = NULL;
1227
1228         if (r_font_use_alpha_textures.integer)
1229                 bytesPerPixel = 1;
1230
1231         if (font->image_font)
1232                 fontface = (FT_Face)font->next->face;
1233         else
1234                 fontface = (FT_Face)font->face;
1235
1236         switch(font->settings->antialias)
1237         {
1238                 case 0:
1239                         switch(font->settings->hinting)
1240                         {
1241                                 case 0:
1242                                         load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1243                                         break;
1244                                 case 1:
1245                                 case 2:
1246                                         load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1247                                         break;
1248                                 default:
1249                                 case 3:
1250                                         load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1251                                         break;
1252                         }
1253                         break;
1254                 default:
1255                 case 1:
1256                         switch(font->settings->hinting)
1257                         {
1258                                 case 0:
1259                                         load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1260                                         break;
1261                                 case 1:
1262                                         load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
1263                                         break;
1264                                 case 2:
1265                                         load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1266                                         break;
1267                                 default:
1268                                 case 3:
1269                                         load_flags = FT_LOAD_TARGET_NORMAL;
1270                                         break;
1271                         }
1272                         break;
1273         }
1274
1275         //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
1276         //if (status)
1277         if (font->image_font && mapstart->intSize < 0)
1278                 mapstart->intSize = mapstart->size;
1279         if (mapstart->intSize < 0)
1280         {
1281                 /*
1282                 mapstart->intSize = mapstart->size;
1283                 while (1)
1284                 {
1285                         if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1286                         {
1287                                 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
1288                                 return false;
1289                         }
1290                         if ((fontface->size->metrics.height>>6) <= mapstart->size)
1291                                 break;
1292                         if (mapstart->intSize < 2)
1293                         {
1294                                 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
1295                                 return false;
1296                         }
1297                         --mapstart->intSize;
1298                 }
1299                 */
1300                 if ((mapstart->intSize = Font_SearchSize(font, fontface, mapstart->size)) <= 0)
1301                         return false;
1302                 Con_DPrintf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
1303         }
1304
1305         if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1306         {
1307                 Con_Printf(CON_ERROR "ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
1308                 return false;
1309         }
1310
1311         map = (ft2_font_map_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
1312         if (!map)
1313         {
1314                 Con_Printf(CON_ERROR "ERROR: Out of memory when loading fontmap for %s\n", font->name);
1315                 return false;
1316         }
1317
1318         // create a totally unique name for this map, then we will use it to make a unique cachepic_t to avoid redundant textures
1319         dpsnprintf(map_identifier, sizeof(map_identifier),
1320                 "%s_cache_%g_%d_%g_%g_%g_%g_%g_%u",
1321                 font->name,
1322                 (double) mapstart->intSize,
1323                 (int) load_flags,
1324                 (double) font->settings->blur,
1325                 (double) font->settings->outline,
1326                 (double) font->settings->shadowx,
1327                 (double) font->settings->shadowy,
1328                 (double) font->settings->shadowz,
1329                 (unsigned) mapidx);
1330
1331         // create a cachepic_t from the data now, or reuse an existing one
1332         if (developer_font.integer)
1333                 Con_Printf("Generating font map %s (size: %.1f MB)\n", map_identifier, mapstart->glyphSize * (256 * 4 / 1048576.0) * mapstart->glyphSize);
1334
1335         Font_Postprocess(font, NULL, 0, bytesPerPixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
1336
1337         // copy over the information
1338         map->size = mapstart->size;
1339         map->intSize = mapstart->intSize;
1340         map->glyphSize = mapstart->glyphSize;
1341         map->sfx = mapstart->sfx;
1342         map->sfy = mapstart->sfy;
1343
1344         pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
1345         data = (unsigned char *)Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
1346         if (!data)
1347         {
1348                 Con_Printf(CON_ERROR "ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
1349                 Mem_Free(map);
1350                 return false;
1351         }
1352         // initialize as white texture with zero alpha
1353         tp = 0;
1354         while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
1355         {
1356                 if (bytesPerPixel == 4)
1357                 {
1358                         data[tp++] = 0xFF;
1359                         data[tp++] = 0xFF;
1360                         data[tp++] = 0xFF;
1361                 }
1362                 data[tp++] = 0x00;
1363         }
1364
1365         memset(map->width_of, 0, sizeof(map->width_of));
1366
1367         // insert the map
1368         map->start = mapidx * FONT_CHARS_PER_MAP;
1369         next = mapstart;
1370         while(next->next && next->next->start < map->start)
1371                 next = next->next;
1372         map->next = next->next;
1373         next->next = map;
1374
1375         gR = 0;
1376         gC = -1;
1377         for (ch = map->start;
1378              ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
1379              ++ch)
1380         {
1381                 FT_ULong glyphIndex;
1382                 int w, h, x, y;
1383                 FT_GlyphSlot glyph;
1384                 FT_Bitmap *bmp;
1385                 unsigned char *imagedata = NULL, *dst, *src;
1386                 glyph_slot_t *mapglyph;
1387                 FT_Face face;
1388                 int pad_l, pad_r, pad_t, pad_b;
1389
1390                 mapch = ch - map->start;
1391
1392                 if (developer_font.integer)
1393                         Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
1394
1395                 ++gC;
1396                 if (gC >= FONT_CHARS_PER_LINE)
1397                 {
1398                         gC -= FONT_CHARS_PER_LINE;
1399                         ++gR;
1400                 }
1401
1402                 if (data)
1403                 {
1404                         imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
1405                         imagedata += gpad_t * pitch + gpad_l * bytesPerPixel;
1406                 }
1407                 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
1408                 // we need the glyphIndex
1409                 face = (FT_Face)font->face;
1410                 usefont = NULL;
1411                 if (font->image_font && mapch == ch && img_fontmap[mapch])
1412                 {
1413                         map->glyphs[mapch].image = true;
1414                         continue;
1415                 }
1416                 glyphIndex = qFT_Get_Char_Index(face, ch);
1417                 if (glyphIndex == 0)
1418                 {
1419                         // by convention, 0 is the "missing-glyph"-glyph
1420                         // try to load from a fallback font
1421                         for(usefont = font->next; usefont != NULL; usefont = usefont->next)
1422                         {
1423                                 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
1424                                         continue;
1425                                 // try that glyph
1426                                 face = (FT_Face)usefont->face;
1427                                 glyphIndex = qFT_Get_Char_Index(face, ch);
1428                                 if (glyphIndex == 0)
1429                                         continue;
1430                                 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1431                                 if (status)
1432                                         continue;
1433                                 break;
1434                         }
1435                         if (!usefont)
1436                         {
1437                                 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1438                                 // now we let it use the "missing-glyph"-glyph
1439                                 face = (FT_Face)font->face;
1440                                 glyphIndex = 0;
1441                         }
1442                 }
1443
1444                 if (!usefont)
1445                 {
1446                         usefont = font;
1447                         face = (FT_Face)font->face;
1448                         status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1449                         if (status)
1450                         {
1451                                 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1452                                 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1453                                 continue;
1454                         }
1455                 }
1456
1457                 glyph = face->glyph;
1458                 bmp = &glyph->bitmap;
1459
1460                 w = bmp->width;
1461                 h = bmp->rows;
1462
1463                 if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
1464                         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);
1465                         if (w > map->glyphSize)
1466                                 w = map->glyphSize - gpad_l - gpad_r;
1467                         if (h > map->glyphSize)
1468                                 h = map->glyphSize;
1469                 }
1470
1471                 if (imagedata)
1472                 {
1473                         switch (bmp->pixel_mode)
1474                         {
1475                         case FT_PIXEL_MODE_MONO:
1476                                 if (developer_font.integer)
1477                                         Con_DPrint("glyphinfo:   Pixel Mode: MONO\n");
1478                                 break;
1479                         case FT_PIXEL_MODE_GRAY2:
1480                                 if (developer_font.integer)
1481                                         Con_DPrint("glyphinfo:   Pixel Mode: GRAY2\n");
1482                                 break;
1483                         case FT_PIXEL_MODE_GRAY4:
1484                                 if (developer_font.integer)
1485                                         Con_DPrint("glyphinfo:   Pixel Mode: GRAY4\n");
1486                                 break;
1487                         case FT_PIXEL_MODE_GRAY:
1488                                 if (developer_font.integer)
1489                                         Con_DPrint("glyphinfo:   Pixel Mode: GRAY\n");
1490                                 break;
1491                         default:
1492                                 if (developer_font.integer)
1493                                         Con_DPrintf("glyphinfo:   Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1494                                 Mem_Free(data);
1495                                 Con_Printf(CON_ERROR "ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1496                                 return false;
1497                         }
1498                         for (y = 0; y < h; ++y)
1499                         {
1500                                 dst = imagedata + y * pitch;
1501                                 src = bmp->buffer + y * bmp->pitch;
1502
1503                                 switch (bmp->pixel_mode)
1504                                 {
1505                                 case FT_PIXEL_MODE_MONO:
1506                                         dst += bytesPerPixel - 1; // shift to alpha byte
1507                                         for (x = 0; x < bmp->width; x += 8)
1508                                         {
1509                                                 unsigned char c = *src++;
1510                                                 *dst = 255 * !!((c & 0x80) >> 7); dst += bytesPerPixel;
1511                                                 *dst = 255 * !!((c & 0x40) >> 6); dst += bytesPerPixel;
1512                                                 *dst = 255 * !!((c & 0x20) >> 5); dst += bytesPerPixel;
1513                                                 *dst = 255 * !!((c & 0x10) >> 4); dst += bytesPerPixel;
1514                                                 *dst = 255 * !!((c & 0x08) >> 3); dst += bytesPerPixel;
1515                                                 *dst = 255 * !!((c & 0x04) >> 2); dst += bytesPerPixel;
1516                                                 *dst = 255 * !!((c & 0x02) >> 1); dst += bytesPerPixel;
1517                                                 *dst = 255 * !!((c & 0x01) >> 0); dst += bytesPerPixel;
1518                                         }
1519                                         break;
1520                                 case FT_PIXEL_MODE_GRAY2:
1521                                         dst += bytesPerPixel - 1; // shift to alpha byte
1522                                         for (x = 0; x < bmp->width; x += 4)
1523                                         {
1524                                                 unsigned char c = *src++;
1525                                                 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytesPerPixel;
1526                                                 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytesPerPixel;
1527                                                 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytesPerPixel;
1528                                                 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytesPerPixel;
1529                                         }
1530                                         break;
1531                                 case FT_PIXEL_MODE_GRAY4:
1532                                         dst += bytesPerPixel - 1; // shift to alpha byte
1533                                         for (x = 0; x < bmp->width; x += 2)
1534                                         {
1535                                                 unsigned char c = *src++;
1536                                                 *dst = ( ((c & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
1537                                                 *dst = ( ((c & 0x0F) ) * 0x11); dst += bytesPerPixel;
1538                                         }
1539                                         break;
1540                                 case FT_PIXEL_MODE_GRAY:
1541                                         // in this case pitch should equal width
1542                                         for (tp = 0; tp < bmp->pitch; ++tp)
1543                                                 dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
1544
1545                                         //memcpy((void*)dst, (void*)src, bmp->pitch);
1546                                         //dst += bmp->pitch;
1547                                         break;
1548                                 default:
1549                                         break;
1550                                 }
1551                         }
1552
1553                         pad_l = gpad_l;
1554                         pad_r = gpad_r;
1555                         pad_t = gpad_t;
1556                         pad_b = gpad_b;
1557                         Font_Postprocess(font, imagedata, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1558                 }
1559                 else
1560                 {
1561                         pad_l = gpad_l;
1562                         pad_r = gpad_r;
1563                         pad_t = gpad_t;
1564                         pad_b = gpad_b;
1565                         Font_Postprocess(font, NULL, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1566                 }
1567
1568
1569                 // now fill map->glyphs[ch - map->start]
1570                 mapglyph = &map->glyphs[mapch];
1571
1572                 {
1573                         // old way
1574                         // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1575
1576                         double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1577                         //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1578                         double advance = (glyph->advance.x / 64.0) / map->size;
1579                         //double mWidth = (glyph->metrics.width >> 6) / map->size;
1580                         //double mHeight = (glyph->metrics.height >> 6) / map->size;
1581
1582                         mapglyph->txmin = ( (double)(gC * map->glyphSize) + (double)(gpad_l - pad_l) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1583                         mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1584                         mapglyph->tymin = ( (double)(gR * map->glyphSize) + (double)(gpad_r - pad_r) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1585                         mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1586                         //mapglyph->vxmin = bearingX;
1587                         //mapglyph->vxmax = bearingX + mWidth;
1588                         mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
1589                         mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
1590                         //mapglyph->vymin = -bearingY;
1591                         //mapglyph->vymax = mHeight - bearingY;
1592                         mapglyph->vymin = (-glyph->bitmap_top - pad_t) / map->size;
1593                         mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
1594                         //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);
1595                         //mapglyph->advance_x = advance * usefont->size;
1596                         //mapglyph->advance_x = advance;
1597                         mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1598                         mapglyph->advance_y = 0;
1599
1600                         if (developer_font.integer)
1601                         {
1602                                 Con_DPrintf("glyphinfo:   Glyph: %lu   at (%i, %i)\n", (unsigned long)ch, gC, gR);
1603                                 Con_DPrintf("glyphinfo:   %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1604                                 if (ch >= 32 && ch <= 128)
1605                                         Con_DPrintf("glyphinfo:   Character: %c\n", (int)ch);
1606                                 Con_DPrintf("glyphinfo:   Vertex info:\n");
1607                                 Con_DPrintf("glyphinfo:     X: ( %f  --  %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1608                                 Con_DPrintf("glyphinfo:     Y: ( %f  --  %f )\n", mapglyph->vymin, mapglyph->vymax);
1609                                 Con_DPrintf("glyphinfo:   Texture info:\n");
1610                                 Con_DPrintf("glyphinfo:     S: ( %f  --  %f )\n", mapglyph->txmin, mapglyph->txmax);
1611                                 Con_DPrintf("glyphinfo:     T: ( %f  --  %f )\n", mapglyph->tymin, mapglyph->tymax);
1612                                 Con_DPrintf("glyphinfo:   Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1613                         }
1614                 }
1615                 map->glyphs[mapch].image = false;
1616         }
1617
1618         {
1619                 int w = map->glyphSize * FONT_CHARS_PER_LINE;
1620                 int h = map->glyphSize * FONT_CHAR_LINES;
1621                 // update the pic returned by Draw_CachePic_Flags earlier to contain our texture
1622                 map->pic = Draw_NewPic(map_identifier, w, h, data, r_font_use_alpha_textures.integer ? TEXTYPE_ALPHA : TEXTYPE_RGBA, TEXF_ALPHA | TEXF_CLAMP | (r_font_compress.integer > 0 ? TEXF_COMPRESS : 0));
1623
1624                 if (r_font_diskcache.integer >= 1)
1625                 {
1626                         // swap to BGRA for tga writing...
1627                         int s = w * h;
1628                         int x;
1629                         int b;
1630                         for (x = 0;x < s;x++)
1631                         {
1632                                 b = data[x*4+0];
1633                                 data[x*4+0] = data[x*4+2];
1634                                 data[x*4+2] = b;
1635                         }
1636                         Image_WriteTGABGRA(va(vabuf, sizeof(vabuf), "%s.tga", map_identifier), w, h, data);
1637 #ifndef USE_GLES2
1638                         if (r_font_compress.integer && Draw_IsPicLoaded(map->pic))
1639                                 R_SaveTextureDDSFile(Draw_GetPicTexture(map->pic), va(vabuf, sizeof(vabuf), "dds/%s.dds", map_identifier), r_texture_dds_save.integer < 2, true);
1640 #endif
1641                 }
1642         }
1643
1644         if(data)
1645                 Mem_Free(data);
1646
1647         if (!Draw_IsPicLoaded(map->pic))
1648         {
1649                 // if the first try isn't successful, keep it with a broken texture
1650                 // otherwise we retry to load it every single frame where ft2 rendering is used
1651                 // this would be bad...
1652                 // only `data' must be freed
1653                 Con_Printf(CON_ERROR "ERROR: Failed to generate texture for font %s size %f map %lu\n",
1654                            font->name, mapstart->size, mapidx);
1655                 return false;
1656         }
1657         if (outmap)
1658                 *outmap = map;
1659         return true;
1660 }
1661
1662 qbool Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
1663 {
1664         if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1665                 return false;
1666         // the first map must have been loaded already
1667         if (!font->font_maps[map_index])
1668                 return false;
1669         return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
1670 }
1671
1672 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1673 {
1674         while (start && start->start + FONT_CHARS_PER_MAP <= ch)
1675                 start = start->next;
1676         if (start && start->start > ch)
1677                 return NULL;
1678         return start;
1679 }