]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - ft2.c
Added Draw_GetPicWidth, Draw_GetPicHeight, Draw_IsPicLoaded functions and moved cache...
[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 = {CVAR_SAVE, "r_font_disable_freetype", "1", "disable freetype support for fonts entirely"};
37 cvar_t r_font_use_alpha_textures = {CVAR_SAVE, "r_font_use_alpha_textures", "0", "use alpha-textures for font rendering, this should safe memory"};
38 cvar_t r_font_size_snapping = {CVAR_SAVE, "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 = {CVAR_SAVE, "r_font_kerning", "1", "Use kerning if available"};
40 cvar_t r_font_diskcache = {CVAR_SAVE, "r_font_diskcache", "0", "save font textures to disk for future loading rather than generating them every time"};
41 cvar_t r_font_compress = {CVAR_SAVE, "r_font_compress", "0", "use texture compression on font textures to save video memory"};
42 cvar_t r_font_nonpoweroftwo = {CVAR_SAVE, "r_font_nonpoweroftwo", "1", "use nonpoweroftwo textures for font (saves memory, potentially slower)"};
43 cvar_t developer_font = {CVAR_SAVE, "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, qboolean 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_UnloadLibrary (&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 qboolean 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_LoadLibrary (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("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("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 qboolean 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 qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font);
499 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only);
500 qboolean 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("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("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("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 qboolean 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("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("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("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("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("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         qboolean need_gauss  = (!pp.buf || pp.blur != fnt->settings->blur || pp.shadowz != fnt->settings->shadowz);
717         qboolean 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 qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
890 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean 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 && vid.support.arb_texture_non_power_of_two))
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("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 qboolean 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 qboolean 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("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 qboolean 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("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("ERROR: no appropriate size found for font %s: %f\n", font->name, size);
1197                         return -1;
1198                 }
1199                 --intSize;
1200         }
1201 }
1202
1203 static qboolean 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("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("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         map->pic = Draw_CachePic_Flags(map_identifier, CACHEPICFLAG_QUIET);
1333         if (developer_font.integer)
1334         {
1335                 if (!Draw_IsPicLoaded(map->pic))
1336                         Con_Printf("Generating font map %s (size: %.1f MB)\n", map_identifier, mapstart->glyphSize * (256 * 4 / 1048576.0) * mapstart->glyphSize);
1337                 else
1338                         Con_Printf("Using cached font map %s (size: %.1f MB)\n", map_identifier, mapstart->glyphSize * (256 * 4 / 1048576.0) * mapstart->glyphSize);
1339         }
1340
1341         Font_Postprocess(font, NULL, 0, bytesPerPixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
1342
1343         // copy over the information
1344         map->size = mapstart->size;
1345         map->intSize = mapstart->intSize;
1346         map->glyphSize = mapstart->glyphSize;
1347         map->sfx = mapstart->sfx;
1348         map->sfy = mapstart->sfy;
1349
1350         pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
1351         if (!Draw_IsPicLoaded(map->pic))
1352         {
1353                 data = (unsigned char *)Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
1354                 if (!data)
1355                 {
1356                         Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
1357                         Mem_Free(map);
1358                         return false;
1359                 }
1360                 // initialize as white texture with zero alpha
1361                 tp = 0;
1362                 while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
1363                 {
1364                         if (bytesPerPixel == 4)
1365                         {
1366                                 data[tp++] = 0xFF;
1367                                 data[tp++] = 0xFF;
1368                                 data[tp++] = 0xFF;
1369                         }
1370                         data[tp++] = 0x00;
1371                 }
1372         }
1373
1374         memset(map->width_of, 0, sizeof(map->width_of));
1375
1376         // insert the map
1377         map->start = mapidx * FONT_CHARS_PER_MAP;
1378         next = mapstart;
1379         while(next->next && next->next->start < map->start)
1380                 next = next->next;
1381         map->next = next->next;
1382         next->next = map;
1383
1384         gR = 0;
1385         gC = -1;
1386         for (ch = map->start;
1387              ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
1388              ++ch)
1389         {
1390                 FT_ULong glyphIndex;
1391                 int w, h, x, y;
1392                 FT_GlyphSlot glyph;
1393                 FT_Bitmap *bmp;
1394                 unsigned char *imagedata = NULL, *dst, *src;
1395                 glyph_slot_t *mapglyph;
1396                 FT_Face face;
1397                 int pad_l, pad_r, pad_t, pad_b;
1398
1399                 mapch = ch - map->start;
1400
1401                 if (developer_font.integer)
1402                         Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
1403
1404                 ++gC;
1405                 if (gC >= FONT_CHARS_PER_LINE)
1406                 {
1407                         gC -= FONT_CHARS_PER_LINE;
1408                         ++gR;
1409                 }
1410
1411                 if (data)
1412                 {
1413                         imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
1414                         imagedata += gpad_t * pitch + gpad_l * bytesPerPixel;
1415                 }
1416                 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
1417                 // we need the glyphIndex
1418                 face = (FT_Face)font->face;
1419                 usefont = NULL;
1420                 if (font->image_font && mapch == ch && img_fontmap[mapch])
1421                 {
1422                         map->glyphs[mapch].image = true;
1423                         continue;
1424                 }
1425                 glyphIndex = qFT_Get_Char_Index(face, ch);
1426                 if (glyphIndex == 0)
1427                 {
1428                         // by convention, 0 is the "missing-glyph"-glyph
1429                         // try to load from a fallback font
1430                         for(usefont = font->next; usefont != NULL; usefont = usefont->next)
1431                         {
1432                                 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
1433                                         continue;
1434                                 // try that glyph
1435                                 face = (FT_Face)usefont->face;
1436                                 glyphIndex = qFT_Get_Char_Index(face, ch);
1437                                 if (glyphIndex == 0)
1438                                         continue;
1439                                 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1440                                 if (status)
1441                                         continue;
1442                                 break;
1443                         }
1444                         if (!usefont)
1445                         {
1446                                 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1447                                 // now we let it use the "missing-glyph"-glyph
1448                                 face = (FT_Face)font->face;
1449                                 glyphIndex = 0;
1450                         }
1451                 }
1452
1453                 if (!usefont)
1454                 {
1455                         usefont = font;
1456                         face = (FT_Face)font->face;
1457                         status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1458                         if (status)
1459                         {
1460                                 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1461                                 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1462                                 continue;
1463                         }
1464                 }
1465
1466                 glyph = face->glyph;
1467                 bmp = &glyph->bitmap;
1468
1469                 w = bmp->width;
1470                 h = bmp->rows;
1471
1472                 if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
1473                         Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
1474                         if (w > map->glyphSize)
1475                                 w = map->glyphSize - gpad_l - gpad_r;
1476                         if (h > map->glyphSize)
1477                                 h = map->glyphSize;
1478                 }
1479
1480                 if (imagedata)
1481                 {
1482                         switch (bmp->pixel_mode)
1483                         {
1484                         case FT_PIXEL_MODE_MONO:
1485                                 if (developer_font.integer)
1486                                         Con_DPrint("glyphinfo:   Pixel Mode: MONO\n");
1487                                 break;
1488                         case FT_PIXEL_MODE_GRAY2:
1489                                 if (developer_font.integer)
1490                                         Con_DPrint("glyphinfo:   Pixel Mode: GRAY2\n");
1491                                 break;
1492                         case FT_PIXEL_MODE_GRAY4:
1493                                 if (developer_font.integer)
1494                                         Con_DPrint("glyphinfo:   Pixel Mode: GRAY4\n");
1495                                 break;
1496                         case FT_PIXEL_MODE_GRAY:
1497                                 if (developer_font.integer)
1498                                         Con_DPrint("glyphinfo:   Pixel Mode: GRAY\n");
1499                                 break;
1500                         default:
1501                                 if (developer_font.integer)
1502                                         Con_DPrintf("glyphinfo:   Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1503                                 Mem_Free(data);
1504                                 Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1505                                 return false;
1506                         }
1507                         for (y = 0; y < h; ++y)
1508                         {
1509                                 dst = imagedata + y * pitch;
1510                                 src = bmp->buffer + y * bmp->pitch;
1511
1512                                 switch (bmp->pixel_mode)
1513                                 {
1514                                 case FT_PIXEL_MODE_MONO:
1515                                         dst += bytesPerPixel - 1; // shift to alpha byte
1516                                         for (x = 0; x < bmp->width; x += 8)
1517                                         {
1518                                                 unsigned char c = *src++;
1519                                                 *dst = 255 * !!((c & 0x80) >> 7); dst += bytesPerPixel;
1520                                                 *dst = 255 * !!((c & 0x40) >> 6); dst += bytesPerPixel;
1521                                                 *dst = 255 * !!((c & 0x20) >> 5); dst += bytesPerPixel;
1522                                                 *dst = 255 * !!((c & 0x10) >> 4); dst += bytesPerPixel;
1523                                                 *dst = 255 * !!((c & 0x08) >> 3); dst += bytesPerPixel;
1524                                                 *dst = 255 * !!((c & 0x04) >> 2); dst += bytesPerPixel;
1525                                                 *dst = 255 * !!((c & 0x02) >> 1); dst += bytesPerPixel;
1526                                                 *dst = 255 * !!((c & 0x01) >> 0); dst += bytesPerPixel;
1527                                         }
1528                                         break;
1529                                 case FT_PIXEL_MODE_GRAY2:
1530                                         dst += bytesPerPixel - 1; // shift to alpha byte
1531                                         for (x = 0; x < bmp->width; x += 4)
1532                                         {
1533                                                 unsigned char c = *src++;
1534                                                 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytesPerPixel;
1535                                                 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytesPerPixel;
1536                                                 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytesPerPixel;
1537                                                 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytesPerPixel;
1538                                         }
1539                                         break;
1540                                 case FT_PIXEL_MODE_GRAY4:
1541                                         dst += bytesPerPixel - 1; // shift to alpha byte
1542                                         for (x = 0; x < bmp->width; x += 2)
1543                                         {
1544                                                 unsigned char c = *src++;
1545                                                 *dst = ( ((c & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
1546                                                 *dst = ( ((c & 0x0F) ) * 0x11); dst += bytesPerPixel;
1547                                         }
1548                                         break;
1549                                 case FT_PIXEL_MODE_GRAY:
1550                                         // in this case pitch should equal width
1551                                         for (tp = 0; tp < bmp->pitch; ++tp)
1552                                                 dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
1553
1554                                         //memcpy((void*)dst, (void*)src, bmp->pitch);
1555                                         //dst += bmp->pitch;
1556                                         break;
1557                                 default:
1558                                         break;
1559                                 }
1560                         }
1561
1562                         pad_l = gpad_l;
1563                         pad_r = gpad_r;
1564                         pad_t = gpad_t;
1565                         pad_b = gpad_b;
1566                         Font_Postprocess(font, imagedata, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1567                 }
1568                 else
1569                 {
1570                         pad_l = gpad_l;
1571                         pad_r = gpad_r;
1572                         pad_t = gpad_t;
1573                         pad_b = gpad_b;
1574                         Font_Postprocess(font, NULL, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1575                 }
1576
1577
1578                 // now fill map->glyphs[ch - map->start]
1579                 mapglyph = &map->glyphs[mapch];
1580
1581                 {
1582                         // old way
1583                         // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1584
1585                         double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1586                         //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1587                         double advance = (glyph->advance.x / 64.0) / map->size;
1588                         //double mWidth = (glyph->metrics.width >> 6) / map->size;
1589                         //double mHeight = (glyph->metrics.height >> 6) / map->size;
1590
1591                         mapglyph->txmin = ( (double)(gC * map->glyphSize) + (double)(gpad_l - pad_l) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1592                         mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1593                         mapglyph->tymin = ( (double)(gR * map->glyphSize) + (double)(gpad_r - pad_r) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1594                         mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1595                         //mapglyph->vxmin = bearingX;
1596                         //mapglyph->vxmax = bearingX + mWidth;
1597                         mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
1598                         mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
1599                         //mapglyph->vymin = -bearingY;
1600                         //mapglyph->vymax = mHeight - bearingY;
1601                         mapglyph->vymin = (-glyph->bitmap_top - pad_t) / map->size;
1602                         mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
1603                         //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);
1604                         //mapglyph->advance_x = advance * usefont->size;
1605                         //mapglyph->advance_x = advance;
1606                         mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1607                         mapglyph->advance_y = 0;
1608
1609                         if (developer_font.integer)
1610                         {
1611                                 Con_DPrintf("glyphinfo:   Glyph: %lu   at (%i, %i)\n", (unsigned long)ch, gC, gR);
1612                                 Con_DPrintf("glyphinfo:   %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1613                                 if (ch >= 32 && ch <= 128)
1614                                         Con_DPrintf("glyphinfo:   Character: %c\n", (int)ch);
1615                                 Con_DPrintf("glyphinfo:   Vertex info:\n");
1616                                 Con_DPrintf("glyphinfo:     X: ( %f  --  %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1617                                 Con_DPrintf("glyphinfo:     Y: ( %f  --  %f )\n", mapglyph->vymin, mapglyph->vymax);
1618                                 Con_DPrintf("glyphinfo:   Texture info:\n");
1619                                 Con_DPrintf("glyphinfo:     S: ( %f  --  %f )\n", mapglyph->txmin, mapglyph->txmax);
1620                                 Con_DPrintf("glyphinfo:     T: ( %f  --  %f )\n", mapglyph->tymin, mapglyph->tymax);
1621                                 Con_DPrintf("glyphinfo:   Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1622                         }
1623                 }
1624                 map->glyphs[mapch].image = false;
1625         }
1626
1627         if (!Draw_IsPicLoaded(map->pic))
1628         {
1629                 int w = map->glyphSize * FONT_CHARS_PER_LINE;
1630                 int h = map->glyphSize * FONT_CHAR_LINES;
1631                 // update the pic returned by Draw_CachePic_Flags earlier to contain our texture
1632                 Draw_NewPic(map_identifier, w, h, data, r_font_use_alpha_textures.integer ? TEXTYPE_ALPHA : TEXTYPE_RGBA, TEXF_ALPHA | (r_font_compress.integer > 0 ? TEXF_COMPRESS : 0));
1633
1634                 if (r_font_diskcache.integer >= 1)
1635                 {
1636                         // swap to BGRA for tga writing...
1637                         int s = w * h;
1638                         int x;
1639                         int b;
1640                         for (x = 0;x < s;x++)
1641                         {
1642                                 b = data[x*4+0];
1643                                 data[x*4+0] = data[x*4+2];
1644                                 data[x*4+2] = b;
1645                         }
1646                         Image_WriteTGABGRA(va(vabuf, sizeof(vabuf), "%s.tga", map_identifier), w, h, data);
1647 #ifndef USE_GLES2
1648                         if (r_font_compress.integer && qglGetCompressedTexImageARB && Draw_IsPicLoaded(map->pic))
1649                                 R_SaveTextureDDSFile(Draw_GetPicTexture(map->pic), va(vabuf, sizeof(vabuf), "dds/%s.dds", map_identifier), r_texture_dds_save.integer < 2, true);
1650 #endif
1651                 }
1652         }
1653
1654         if(data)
1655                 Mem_Free(data);
1656
1657         if (!Draw_IsPicLoaded(map->pic))
1658         {
1659                 // if the first try isn't successful, keep it with a broken texture
1660                 // otherwise we retry to load it every single frame where ft2 rendering is used
1661                 // this would be bad...
1662                 // only `data' must be freed
1663                 Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
1664                            font->name, mapstart->size, mapidx);
1665                 return false;
1666         }
1667         if (outmap)
1668                 *outmap = map;
1669         return true;
1670 }
1671
1672 qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
1673 {
1674         if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1675                 return false;
1676         // the first map must have been loaded already
1677         if (!font->font_maps[map_index])
1678                 return false;
1679         return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
1680 }
1681
1682 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1683 {
1684         while (start && start->start + FONT_CHARS_PER_MAP <= ch)
1685                 start = start->next;
1686         if (start && start->start > ch)
1687                 return NULL;
1688         return start;
1689 }