]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - ft2.c
Netconn: when an encrypted connection is used, randomly set one or more of three...
[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
633         // try load direct file
634         memcpy(filename, name, namelen+1);
635         data = fontfilecache_LoadFile(filename, false, &datasize);
636         // try load .ttf
637         if (!data)
638         {
639                 memcpy(filename + namelen, ".ttf", 5);
640                 data = fontfilecache_LoadFile(filename, false, &datasize);
641         }
642         // try load .otf
643         if (!data)
644         {
645                 memcpy(filename + namelen, ".otf", 5);
646                 data = fontfilecache_LoadFile(filename, false, &datasize);
647         }
648         // try load .pfb/afm
649         if (!data)
650         {
651                 ft2_attachment_t afm;
652
653                 memcpy(filename + namelen, ".pfb", 5);
654                 data = fontfilecache_LoadFile(filename, false, &datasize);
655
656                 if (data)
657                 {
658                         memcpy(filename + namelen, ".afm", 5);
659                         afm.data = fontfilecache_LoadFile(filename, false, &afm.size);
660
661                         if (afm.data)
662                                 Font_Attach(font, &afm);
663                 }
664         }
665         if (!data)
666         {
667                 // FS_LoadFile being not-quiet should print an error :)
668                 return false;
669         }
670         Con_DPrintf("Loading font %s face %i...\n", filename, _face);
671
672         status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
673         if (status && _face != 0)
674         {
675                 Con_Printf("Failed to load face %i of %s. Falling back to face 0\n", _face, name);
676                 _face = 0;
677                 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
678         }
679         font->data = data;
680         if (status)
681         {
682                 Con_Printf("ERROR: can't create face for %s\n"
683                            "Error %i\n", // TODO: error strings
684                            name, status);
685                 Font_UnloadFont(font);
686                 return false;
687         }
688
689         // add the attachments
690         for (i = 0; i < font->attachmentcount; ++i)
691         {
692                 FT_Open_Args args;
693                 memset(&args, 0, sizeof(args));
694                 args.flags = FT_OPEN_MEMORY;
695                 args.memory_base = (const FT_Byte*)font->attachments[i].data;
696                 args.memory_size = font->attachments[i].size;
697                 if (qFT_Attach_Stream((FT_Face)font->face, &args))
698                         Con_Printf("Failed to add attachment %u to %s\n", (unsigned)i, font->name);
699         }
700
701         memcpy(font->name, name, namelen+1);
702         font->image_font = false;
703         font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
704         return true;
705 }
706
707 static void Font_Postprocess_Update(ft2_font_t *fnt, int bpp, int w, int h)
708 {
709         int needed, x, y;
710         float gausstable[2*POSTPROCESS_MAXRADIUS+1];
711         qboolean need_gauss  = (!pp.buf || pp.blur != fnt->settings->blur || pp.shadowz != fnt->settings->shadowz);
712         qboolean need_circle = (!pp.buf || pp.outline != fnt->settings->outline || pp.shadowx != fnt->settings->shadowx || pp.shadowy != fnt->settings->shadowy);
713         pp.blur = fnt->settings->blur;
714         pp.outline = fnt->settings->outline;
715         pp.shadowx = fnt->settings->shadowx;
716         pp.shadowy = fnt->settings->shadowy;
717         pp.shadowz = fnt->settings->shadowz;
718         pp.outlinepadding_l = bound(0, ceil(pp.outline - pp.shadowx), POSTPROCESS_MAXRADIUS);
719         pp.outlinepadding_r = bound(0, ceil(pp.outline + pp.shadowx), POSTPROCESS_MAXRADIUS);
720         pp.outlinepadding_t = bound(0, ceil(pp.outline - pp.shadowy), POSTPROCESS_MAXRADIUS);
721         pp.outlinepadding_b = bound(0, ceil(pp.outline + pp.shadowy), POSTPROCESS_MAXRADIUS);
722         pp.blurpadding_lt = bound(0, ceil(pp.blur - pp.shadowz), POSTPROCESS_MAXRADIUS);
723         pp.blurpadding_rb = bound(0, ceil(pp.blur + pp.shadowz), POSTPROCESS_MAXRADIUS);
724         pp.padding_l = pp.blurpadding_lt + pp.outlinepadding_l;
725         pp.padding_r = pp.blurpadding_rb + pp.outlinepadding_r;
726         pp.padding_t = pp.blurpadding_lt + pp.outlinepadding_t;
727         pp.padding_b = pp.blurpadding_rb + pp.outlinepadding_b;
728         if(need_gauss)
729         {
730                 float sum = 0;
731                 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
732                         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));
733                 for(x = -pp.blurpadding_rb; x <= pp.blurpadding_lt; ++x)
734                         sum += gausstable[POSTPROCESS_MAXRADIUS+x];
735                 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
736                         pp.gausstable[POSTPROCESS_MAXRADIUS+x] = floor(gausstable[POSTPROCESS_MAXRADIUS+x] / sum * 255 + 0.5);
737         }
738         if(need_circle)
739         {
740                 for(y = -POSTPROCESS_MAXRADIUS; y <= POSTPROCESS_MAXRADIUS; ++y)
741                         for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
742                         {
743                                 float d = pp.outline + 1 - sqrt(pow(x + pp.shadowx, 2) + pow(y + pp.shadowy, 2));
744                                 pp.circlematrix[POSTPROCESS_MAXRADIUS+y][POSTPROCESS_MAXRADIUS+x] = (d >= 1) ? 255 : (d <= 0) ? 0 : floor(d * 255 + 0.5);
745                         }
746         }
747         pp.bufwidth = w + pp.padding_l + pp.padding_r;
748         pp.bufheight = h + pp.padding_t + pp.padding_b;
749         pp.bufpitch = pp.bufwidth;
750         needed = pp.bufwidth * pp.bufheight;
751         if(!pp.buf || pp.bufsize < needed * 2)
752         {
753                 if(pp.buf)
754                         Mem_Free(pp.buf);
755                 pp.bufsize = needed * 4;
756                 pp.buf = (unsigned char *)Mem_Alloc(font_mempool, pp.bufsize);
757                 pp.buf2 = pp.buf + needed;
758         }
759 }
760
761 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)
762 {
763         int x, y;
764
765         // calculate gauss table
766         Font_Postprocess_Update(fnt, bpp, w, h);
767
768         if(imagedata)
769         {
770                 // enlarge buffer
771                 // perform operation, not exceeding the passed padding values,
772                 // but possibly reducing them
773                 *pad_l = min(*pad_l, pp.padding_l);
774                 *pad_r = min(*pad_r, pp.padding_r);
775                 *pad_t = min(*pad_t, pp.padding_t);
776                 *pad_b = min(*pad_b, pp.padding_b);
777
778                 // outline the font (RGBA only)
779                 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
780                 {
781                         // this is like mplayer subtitle rendering
782                         // bbuffer, bitmap buffer: this is our font
783                         // abuffer, alpha buffer: this is pp.buf
784                         // tmp: this is pp.buf2
785
786                         // create outline buffer
787                         memset(pp.buf, 0, pp.bufwidth * pp.bufheight);
788                         for(y = -*pad_t; y < h + *pad_b; ++y)
789                                 for(x = -*pad_l; x < w + *pad_r; ++x)
790                                 {
791                                         int x1 = max(-x, -pp.outlinepadding_r);
792                                         int y1 = max(-y, -pp.outlinepadding_b);
793                                         int x2 = min(pp.outlinepadding_l, w-1-x);
794                                         int y2 = min(pp.outlinepadding_t, h-1-y);
795                                         int mx, my;
796                                         int cur = 0;
797                                         int highest = 0;
798                                         for(my = y1; my <= y2; ++my)
799                                                 for(mx = x1; mx <= x2; ++mx)
800                                                 {
801                                                         cur = pp.circlematrix[POSTPROCESS_MAXRADIUS+my][POSTPROCESS_MAXRADIUS+mx] * (int)imagedata[(x+mx) * bpp + pitch * (y+my) + (bpp - 1)];
802                                                         if(cur > highest)
803                                                                 highest = cur;
804                                                 }
805                                         pp.buf[((x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t))] = (highest + 128) / 255;
806                                 }
807
808                         // blur the outline buffer
809                         if(pp.blur > 0 || pp.shadowz != 0)
810                         {
811                                 // horizontal blur
812                                 for(y = 0; y < pp.bufheight; ++y)
813                                         for(x = 0; x < pp.bufwidth; ++x)
814                                         {
815                                                 int x1 = max(-x, -pp.blurpadding_rb);
816                                                 int x2 = min(pp.blurpadding_lt, pp.bufwidth-1-x);
817                                                 int mx;
818                                                 int blurred = 0;
819                                                 for(mx = x1; mx <= x2; ++mx)
820                                                         blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+mx] * (int)pp.buf[(x+mx) + pp.bufpitch * y];
821                                                 pp.buf2[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
822                                         }
823
824                                 // vertical blur
825                                 for(y = 0; y < pp.bufheight; ++y)
826                                         for(x = 0; x < pp.bufwidth; ++x)
827                                         {
828                                                 int y1 = max(-y, -pp.blurpadding_rb);
829                                                 int y2 = min(pp.blurpadding_lt, pp.bufheight-1-y);
830                                                 int my;
831                                                 int blurred = 0;
832                                                 for(my = y1; my <= y2; ++my)
833                                                         blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+my] * (int)pp.buf2[x + pp.bufpitch * (y+my)];
834                                                 pp.buf[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
835                                         }
836                         }
837
838                         // paste the outline below the font
839                         for(y = -*pad_t; y < h + *pad_b; ++y)
840                                 for(x = -*pad_l; x < w + *pad_r; ++x)
841                                 {
842                                         unsigned char outlinealpha = pp.buf[(x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t)];
843                                         if(outlinealpha > 0)
844                                         {
845                                                 unsigned char oldalpha = imagedata[x * bpp + pitch * y + (bpp - 1)];
846                                                 // a' = 1 - (1 - a1) (1 - a2)
847                                                 unsigned char newalpha = 255 - ((255 - (int)outlinealpha) * (255 - (int)oldalpha)) / 255; // this is >= oldalpha
848                                                 // c' = (a2 c2 - a1 a2 c1 + a1 c1) / a' = (a2 c2 + a1 (1 - a2) c1) / a'
849                                                 unsigned char oldfactor     = (255 * (int)oldalpha) / newalpha;
850                                                 //unsigned char outlinefactor = ((255 - oldalpha) * (int)outlinealpha) / newalpha;
851                                                 int i;
852                                                 for(i = 0; i < bpp-1; ++i)
853                                                 {
854                                                         unsigned char c = imagedata[x * bpp + pitch * y + i];
855                                                         c = (c * (int)oldfactor) / 255 /* + outlinecolor[i] * (int)outlinefactor */;
856                                                         imagedata[x * bpp + pitch * y + i] = c;
857                                                 }
858                                                 imagedata[x * bpp + pitch * y + (bpp - 1)] = newalpha;
859                                         }
860                                         //imagedata[x * bpp + pitch * y + (bpp - 1)] |= 0x80;
861                                 }
862                 }
863         }
864         else if(pitch)
865         {
866                 // perform operation, not exceeding the passed padding values,
867                 // but possibly reducing them
868                 *pad_l = min(*pad_l, pp.padding_l);
869                 *pad_r = min(*pad_r, pp.padding_r);
870                 *pad_t = min(*pad_t, pp.padding_t);
871                 *pad_b = min(*pad_b, pp.padding_b);
872         }
873         else
874         {
875                 // just calculate parameters
876                 *pad_l = pp.padding_l;
877                 *pad_r = pp.padding_r;
878                 *pad_t = pp.padding_t;
879                 *pad_b = pp.padding_b;
880         }
881 }
882
883 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size);
884 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
885 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only)
886 {
887         int map_index;
888         ft2_font_map_t *fmap, temp;
889         int gpad_l, gpad_r, gpad_t, gpad_b;
890
891         if (!(size > 0.001f && size < 1000.0f))
892                 size = 0;
893
894         if (!size)
895                 size = 16;
896         if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
897                 return false;
898
899         for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
900         {
901                 if (!font->font_maps[map_index])
902                         break;
903                 // if a similar size has already been loaded, ignore this one
904                 //abs(font->font_maps[map_index]->size - size) < 4
905                 if (font->font_maps[map_index]->size == size)
906                         return true;
907         }
908
909         if (map_index >= MAX_FONT_SIZES)
910                 return false;
911
912         if (check_only) {
913                 FT_Face fontface;
914                 if (font->image_font)
915                         fontface = (FT_Face)font->next->face;
916                 else
917                         fontface = (FT_Face)font->face;
918                 return (Font_SearchSize(font, fontface, size) > 0);
919         }
920
921         Font_Postprocess(font, NULL, 0, 4, size*2, size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
922
923         memset(&temp, 0, sizeof(temp));
924         temp.size = size;
925         temp.glyphSize = size*2 + max(gpad_l + gpad_r, gpad_t + gpad_b);
926         if (!(r_font_nonpoweroftwo.integer && vid.support.arb_texture_non_power_of_two))
927                 temp.glyphSize = CeilPowerOf2(temp.glyphSize);
928         temp.sfx = (1.0/64.0)/(double)size;
929         temp.sfy = (1.0/64.0)/(double)size;
930         temp.intSize = -1; // negative value: LoadMap must search now :)
931         if (!Font_LoadMap(font, &temp, 0, &fmap))
932         {
933                 Con_Printf("ERROR: can't load the first character map for %s\n"
934                            "This is fatal\n",
935                            font->name);
936                 Font_UnloadFont(font);
937                 return false;
938         }
939         font->font_maps[map_index] = temp.next;
940
941         fmap->sfx = temp.sfx;
942         fmap->sfy = temp.sfy;
943
944         // load the default kerning vector:
945         if (font->has_kerning)
946         {
947                 Uchar l, r;
948                 FT_Vector kernvec;
949                 for (l = 0; l < 256; ++l)
950                 {
951                         for (r = 0; r < 256; ++r)
952                         {
953                                 FT_ULong ul, ur;
954                                 ul = qFT_Get_Char_Index((FT_Face)font->face, l);
955                                 ur = qFT_Get_Char_Index((FT_Face)font->face, r);
956                                 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
957                                 {
958                                         fmap->kerning.kerning[l][r][0] = 0;
959                                         fmap->kerning.kerning[l][r][1] = 0;
960                                 }
961                                 else
962                                 {
963                                         fmap->kerning.kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
964                                         fmap->kerning.kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
965                                 }
966                         }
967                 }
968         }
969         return true;
970 }
971
972 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
973 {
974         int match = -1;
975         float value = 1000000;
976         float nval;
977         int matchsize = -10000;
978         int m;
979         float fsize_x, fsize_y;
980         ft2_font_map_t **maps = font->font_maps;
981
982         fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value;
983         if(outw && *outw)
984                 fsize_x = *outw * vid.width / vid_conwidth.value;
985         if(outh && *outh)
986                 fsize_y = *outh * vid.height / vid_conheight.value;
987
988         if (fsize_x < 0)
989         {
990                 if(fsize_y < 0)
991                         fsize_x = fsize_y = 16;
992                 else
993                         fsize_x = fsize_y;
994         }
995         else
996         {
997                 if(fsize_y < 0)
998                         fsize_y = fsize_x;
999         }
1000
1001         for (m = 0; m < MAX_FONT_SIZES; ++m)
1002         {
1003                 if (!maps[m])
1004                         continue;
1005                 // "round up" to the bigger size if two equally-valued matches exist
1006                 nval = 0.5 * (fabs(maps[m]->size - fsize_x) + fabs(maps[m]->size - fsize_y));
1007                 if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
1008                 {
1009                         value = nval;
1010                         match = m;
1011                         matchsize = maps[m]->size;
1012                         if (value == 0) // there is no better match
1013                                 break;
1014                 }
1015         }
1016         if (value <= r_font_size_snapping.value)
1017         {
1018                 // do NOT keep the aspect for perfect rendering
1019                 if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
1020                 if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width;
1021         }
1022         return match;
1023 }
1024
1025 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
1026 {
1027         if (index < 0 || index >= MAX_FONT_SIZES)
1028                 return NULL;
1029         return font->font_maps[index];
1030 }
1031
1032 static qboolean Font_SetSize(ft2_font_t *font, float w, float h)
1033 {
1034         if (font->currenth == h &&
1035             ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
1036              font->currentw == w)) // same size has been requested
1037         {
1038                 return true;
1039         }
1040         // sorry, but freetype doesn't seem to care about other sizes
1041         w = (int)w;
1042         h = (int)h;
1043         if (font->image_font)
1044         {
1045                 if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
1046                         return false;
1047         }
1048         else
1049         {
1050                 if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
1051                         return false;
1052         }
1053         font->currentw = w;
1054         font->currenth = h;
1055         return true;
1056 }
1057
1058 qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
1059 {
1060         ft2_font_map_t *fmap;
1061         if (!font->has_kerning || !r_font_kerning.integer)
1062                 return false;
1063         if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1064                 return false;
1065         fmap = font->font_maps[map_index];
1066         if (!fmap)
1067                 return false;
1068         if (left < 256 && right < 256)
1069         {
1070                 //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
1071                 // quick-kerning, be aware of the size: scale it
1072                 if (outx) *outx = fmap->kerning.kerning[left][right][0];// * (w / (float)fmap->size);
1073                 if (outy) *outy = fmap->kerning.kerning[left][right][1];// * (h / (float)fmap->size);
1074                 return true;
1075         }
1076         else
1077         {
1078                 FT_Vector kernvec;
1079                 FT_ULong ul, ur;
1080
1081                 //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
1082 #if 0
1083                 if (!Font_SetSize(font, w, h))
1084                 {
1085                         // this deserves an error message
1086                         Con_Printf("Failed to get kerning for %s\n", font->name);
1087                         return false;
1088                 }
1089                 ul = qFT_Get_Char_Index(font->face, left);
1090                 ur = qFT_Get_Char_Index(font->face, right);
1091                 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
1092                 {
1093                         if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);
1094                         if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);
1095                         return true;
1096                 }
1097 #endif
1098                 if (!Font_SetSize(font, fmap->intSize, fmap->intSize))
1099                 {
1100                         // this deserves an error message
1101                         Con_Printf("Failed to get kerning for %s\n", font->name);
1102                         return false;
1103                 }
1104                 ul = qFT_Get_Char_Index((FT_Face)font->face, left);
1105                 ur = qFT_Get_Char_Index((FT_Face)font->face, right);
1106                 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
1107                 {
1108                         if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);// * (w / (float)fmap->size);
1109                         if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);// * (h / (float)fmap->size);
1110                         return true;
1111                 }
1112                 return false;
1113         }
1114 }
1115
1116 qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
1117 {
1118         return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
1119 }
1120
1121 static void UnloadMapRec(ft2_font_map_t *map)
1122 {
1123         if (map->pic)
1124         {
1125                 //Draw_FreePic(map->pic); // FIXME: refcounting needed...
1126                 map->pic = NULL;
1127         }
1128         if (map->next)
1129                 UnloadMapRec(map->next);
1130         Mem_Free(map);
1131 }
1132
1133 void Font_UnloadFont(ft2_font_t *font)
1134 {
1135         int i;
1136
1137         // unload fallbacks
1138         if(font->next)
1139                 Font_UnloadFont(font->next);
1140
1141         if (font->attachments && font->attachmentcount)
1142         {
1143                 for (i = 0; i < (int)font->attachmentcount; ++i) {
1144                         if (font->attachments[i].data)
1145                                 fontfilecache_Free(font->attachments[i].data);
1146                 }
1147                 Mem_Free(font->attachments);
1148                 font->attachmentcount = 0;
1149                 font->attachments = NULL;
1150         }
1151         for (i = 0; i < MAX_FONT_SIZES; ++i)
1152         {
1153                 if (font->font_maps[i])
1154                 {
1155                         UnloadMapRec(font->font_maps[i]);
1156                         font->font_maps[i] = NULL;
1157                 }
1158         }
1159 #ifndef DP_FREETYPE_STATIC
1160         if (ft2_dll)
1161 #else
1162         if (!r_font_disable_freetype.integer)
1163 #endif
1164         {
1165                 if (font->face)
1166                 {
1167                         qFT_Done_Face((FT_Face)font->face);
1168                         font->face = NULL;
1169                 }
1170         }
1171         if (font->data) {
1172             fontfilecache_Free(font->data);
1173             font->data = NULL;
1174         }
1175 }
1176
1177 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size)
1178 {
1179         float intSize = size;
1180         while (1)
1181         {
1182                 if (!Font_SetSize(font, intSize, intSize))
1183                 {
1184                         Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, size, intSize);
1185                         return -1;
1186                 }
1187                 if ((fontface->size->metrics.height>>6) <= size)
1188                         return intSize;
1189                 if (intSize < 2)
1190                 {
1191                         Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, size);
1192                         return -1;
1193                 }
1194                 --intSize;
1195         }
1196 }
1197
1198 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
1199 {
1200         char map_identifier[MAX_QPATH];
1201         unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
1202         unsigned char *data = NULL;
1203         FT_ULong ch, mapch;
1204         int status;
1205         int tp;
1206         FT_Int32 load_flags;
1207         int gpad_l, gpad_r, gpad_t, gpad_b;
1208         char vabuf[1024];
1209
1210         int pitch;
1211         int gR, gC; // glyph position: row and column
1212
1213         ft2_font_map_t *map, *next;
1214         ft2_font_t *usefont;
1215
1216         FT_Face fontface;
1217
1218         int bytesPerPixel = 4; // change the conversion loop too if you change this!
1219
1220         if (outmap)
1221                 *outmap = NULL;
1222
1223         if (r_font_use_alpha_textures.integer)
1224                 bytesPerPixel = 1;
1225
1226         if (font->image_font)
1227                 fontface = (FT_Face)font->next->face;
1228         else
1229                 fontface = (FT_Face)font->face;
1230
1231         switch(font->settings->antialias)
1232         {
1233                 case 0:
1234                         switch(font->settings->hinting)
1235                         {
1236                                 case 0:
1237                                         load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1238                                         break;
1239                                 case 1:
1240                                 case 2:
1241                                         load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1242                                         break;
1243                                 default:
1244                                 case 3:
1245                                         load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1246                                         break;
1247                         }
1248                         break;
1249                 default:
1250                 case 1:
1251                         switch(font->settings->hinting)
1252                         {
1253                                 case 0:
1254                                         load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1255                                         break;
1256                                 case 1:
1257                                         load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
1258                                         break;
1259                                 case 2:
1260                                         load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1261                                         break;
1262                                 default:
1263                                 case 3:
1264                                         load_flags = FT_LOAD_TARGET_NORMAL;
1265                                         break;
1266                         }
1267                         break;
1268         }
1269
1270         //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
1271         //if (status)
1272         if (font->image_font && mapstart->intSize < 0)
1273                 mapstart->intSize = mapstart->size;
1274         if (mapstart->intSize < 0)
1275         {
1276                 /*
1277                 mapstart->intSize = mapstart->size;
1278                 while (1)
1279                 {
1280                         if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1281                         {
1282                                 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
1283                                 return false;
1284                         }
1285                         if ((fontface->size->metrics.height>>6) <= mapstart->size)
1286                                 break;
1287                         if (mapstart->intSize < 2)
1288                         {
1289                                 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
1290                                 return false;
1291                         }
1292                         --mapstart->intSize;
1293                 }
1294                 */
1295                 if ((mapstart->intSize = Font_SearchSize(font, fontface, mapstart->size)) <= 0)
1296                         return false;
1297                 Con_DPrintf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
1298         }
1299
1300         if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1301         {
1302                 Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
1303                 return false;
1304         }
1305
1306         map = (ft2_font_map_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
1307         if (!map)
1308         {
1309                 Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
1310                 return false;
1311         }
1312
1313         // create a totally unique name for this map, then we will use it to make a unique cachepic_t to avoid redundant textures
1314         dpsnprintf(map_identifier, sizeof(map_identifier),
1315                 "%s_cache_%g_%d_%g_%g_%g_%g_%g_%u",
1316                 font->name,
1317                 (double) mapstart->intSize,
1318                 (int) load_flags,
1319                 (double) font->settings->blur,
1320                 (double) font->settings->outline,
1321                 (double) font->settings->shadowx,
1322                 (double) font->settings->shadowy,
1323                 (double) font->settings->shadowz,
1324                 (unsigned) mapidx);
1325
1326         // create a cachepic_t from the data now, or reuse an existing one
1327         map->pic = Draw_CachePic_Flags(map_identifier, CACHEPICFLAG_QUIET);
1328         if (developer_font.integer)
1329         {
1330                 if (map->pic->tex == r_texture_notexture)
1331                         Con_Printf("Generating font map %s (size: %.1f MB)\n", map_identifier, mapstart->glyphSize * (256 * 4 / 1048576.0) * mapstart->glyphSize);
1332                 else
1333                         Con_Printf("Using cached font map %s (size: %.1f MB)\n", map_identifier, mapstart->glyphSize * (256 * 4 / 1048576.0) * mapstart->glyphSize);
1334         }
1335
1336         Font_Postprocess(font, NULL, 0, bytesPerPixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
1337
1338         // copy over the information
1339         map->size = mapstart->size;
1340         map->intSize = mapstart->intSize;
1341         map->glyphSize = mapstart->glyphSize;
1342         map->sfx = mapstart->sfx;
1343         map->sfy = mapstart->sfy;
1344
1345         pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
1346         if (map->pic->tex == r_texture_notexture)
1347         {
1348                 data = (unsigned char *)Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
1349                 if (!data)
1350                 {
1351                         Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
1352                         Mem_Free(map);
1353                         return false;
1354                 }
1355                 // initialize as white texture with zero alpha
1356                 tp = 0;
1357                 while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
1358                 {
1359                         if (bytesPerPixel == 4)
1360                         {
1361                                 data[tp++] = 0xFF;
1362                                 data[tp++] = 0xFF;
1363                                 data[tp++] = 0xFF;
1364                         }
1365                         data[tp++] = 0x00;
1366                 }
1367         }
1368
1369         memset(map->width_of, 0, sizeof(map->width_of));
1370
1371         // insert the map
1372         map->start = mapidx * FONT_CHARS_PER_MAP;
1373         next = mapstart;
1374         while(next->next && next->next->start < map->start)
1375                 next = next->next;
1376         map->next = next->next;
1377         next->next = map;
1378
1379         gR = 0;
1380         gC = -1;
1381         for (ch = map->start;
1382              ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
1383              ++ch)
1384         {
1385                 FT_ULong glyphIndex;
1386                 int w, h, x, y;
1387                 FT_GlyphSlot glyph;
1388                 FT_Bitmap *bmp;
1389                 unsigned char *imagedata = NULL, *dst, *src;
1390                 glyph_slot_t *mapglyph;
1391                 FT_Face face;
1392                 int pad_l, pad_r, pad_t, pad_b;
1393
1394                 mapch = ch - map->start;
1395
1396                 if (developer_font.integer)
1397                         Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
1398
1399                 ++gC;
1400                 if (gC >= FONT_CHARS_PER_LINE)
1401                 {
1402                         gC -= FONT_CHARS_PER_LINE;
1403                         ++gR;
1404                 }
1405
1406                 if (data)
1407                 {
1408                         imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
1409                         imagedata += gpad_t * pitch + gpad_l * bytesPerPixel;
1410                 }
1411                 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
1412                 // we need the glyphIndex
1413                 face = (FT_Face)font->face;
1414                 usefont = NULL;
1415                 if (font->image_font && mapch == ch && img_fontmap[mapch])
1416                 {
1417                         map->glyphs[mapch].image = true;
1418                         continue;
1419                 }
1420                 glyphIndex = qFT_Get_Char_Index(face, ch);
1421                 if (glyphIndex == 0)
1422                 {
1423                         // by convention, 0 is the "missing-glyph"-glyph
1424                         // try to load from a fallback font
1425                         for(usefont = font->next; usefont != NULL; usefont = usefont->next)
1426                         {
1427                                 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
1428                                         continue;
1429                                 // try that glyph
1430                                 face = (FT_Face)usefont->face;
1431                                 glyphIndex = qFT_Get_Char_Index(face, ch);
1432                                 if (glyphIndex == 0)
1433                                         continue;
1434                                 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1435                                 if (status)
1436                                         continue;
1437                                 break;
1438                         }
1439                         if (!usefont)
1440                         {
1441                                 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1442                                 // now we let it use the "missing-glyph"-glyph
1443                                 face = (FT_Face)font->face;
1444                                 glyphIndex = 0;
1445                         }
1446                 }
1447
1448                 if (!usefont)
1449                 {
1450                         usefont = font;
1451                         face = (FT_Face)font->face;
1452                         status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1453                         if (status)
1454                         {
1455                                 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1456                                 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1457                                 continue;
1458                         }
1459                 }
1460
1461                 glyph = face->glyph;
1462                 bmp = &glyph->bitmap;
1463
1464                 w = bmp->width;
1465                 h = bmp->rows;
1466
1467                 if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
1468                         Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
1469                         if (w > map->glyphSize)
1470                                 w = map->glyphSize - gpad_l - gpad_r;
1471                         if (h > map->glyphSize)
1472                                 h = map->glyphSize;
1473                 }
1474
1475                 if (imagedata)
1476                 {
1477                         switch (bmp->pixel_mode)
1478                         {
1479                         case FT_PIXEL_MODE_MONO:
1480                                 if (developer_font.integer)
1481                                         Con_DPrint("glyphinfo:   Pixel Mode: MONO\n");
1482                                 break;
1483                         case FT_PIXEL_MODE_GRAY2:
1484                                 if (developer_font.integer)
1485                                         Con_DPrint("glyphinfo:   Pixel Mode: GRAY2\n");
1486                                 break;
1487                         case FT_PIXEL_MODE_GRAY4:
1488                                 if (developer_font.integer)
1489                                         Con_DPrint("glyphinfo:   Pixel Mode: GRAY4\n");
1490                                 break;
1491                         case FT_PIXEL_MODE_GRAY:
1492                                 if (developer_font.integer)
1493                                         Con_DPrint("glyphinfo:   Pixel Mode: GRAY\n");
1494                                 break;
1495                         default:
1496                                 if (developer_font.integer)
1497                                         Con_DPrintf("glyphinfo:   Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1498                                 Mem_Free(data);
1499                                 Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1500                                 return false;
1501                         }
1502                         for (y = 0; y < h; ++y)
1503                         {
1504                                 dst = imagedata + y * pitch;
1505                                 src = bmp->buffer + y * bmp->pitch;
1506
1507                                 switch (bmp->pixel_mode)
1508                                 {
1509                                 case FT_PIXEL_MODE_MONO:
1510                                         dst += bytesPerPixel - 1; // shift to alpha byte
1511                                         for (x = 0; x < bmp->width; x += 8)
1512                                         {
1513                                                 unsigned char ch = *src++;
1514                                                 *dst = 255 * !!((ch & 0x80) >> 7); dst += bytesPerPixel;
1515                                                 *dst = 255 * !!((ch & 0x40) >> 6); dst += bytesPerPixel;
1516                                                 *dst = 255 * !!((ch & 0x20) >> 5); dst += bytesPerPixel;
1517                                                 *dst = 255 * !!((ch & 0x10) >> 4); dst += bytesPerPixel;
1518                                                 *dst = 255 * !!((ch & 0x08) >> 3); dst += bytesPerPixel;
1519                                                 *dst = 255 * !!((ch & 0x04) >> 2); dst += bytesPerPixel;
1520                                                 *dst = 255 * !!((ch & 0x02) >> 1); dst += bytesPerPixel;
1521                                                 *dst = 255 * !!((ch & 0x01) >> 0); dst += bytesPerPixel;
1522                                         }
1523                                         break;
1524                                 case FT_PIXEL_MODE_GRAY2:
1525                                         dst += bytesPerPixel - 1; // shift to alpha byte
1526                                         for (x = 0; x < bmp->width; x += 4)
1527                                         {
1528                                                 unsigned char ch = *src++;
1529                                                 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1530                                                 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1531                                                 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1532                                                 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1533                                         }
1534                                         break;
1535                                 case FT_PIXEL_MODE_GRAY4:
1536                                         dst += bytesPerPixel - 1; // shift to alpha byte
1537                                         for (x = 0; x < bmp->width; x += 2)
1538                                         {
1539                                                 unsigned char ch = *src++;
1540                                                 *dst = ( ((ch & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
1541                                                 *dst = ( ((ch & 0x0F) ) * 0x11); dst += bytesPerPixel;
1542                                         }
1543                                         break;
1544                                 case FT_PIXEL_MODE_GRAY:
1545                                         // in this case pitch should equal width
1546                                         for (tp = 0; tp < bmp->pitch; ++tp)
1547                                                 dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
1548
1549                                         //memcpy((void*)dst, (void*)src, bmp->pitch);
1550                                         //dst += bmp->pitch;
1551                                         break;
1552                                 default:
1553                                         break;
1554                                 }
1555                         }
1556
1557                         pad_l = gpad_l;
1558                         pad_r = gpad_r;
1559                         pad_t = gpad_t;
1560                         pad_b = gpad_b;
1561                         Font_Postprocess(font, imagedata, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1562                 }
1563                 else
1564                 {
1565                         pad_l = gpad_l;
1566                         pad_r = gpad_r;
1567                         pad_t = gpad_t;
1568                         pad_b = gpad_b;
1569                         Font_Postprocess(font, NULL, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1570                 }
1571
1572
1573                 // now fill map->glyphs[ch - map->start]
1574                 mapglyph = &map->glyphs[mapch];
1575
1576                 {
1577                         // old way
1578                         // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1579
1580                         double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1581                         //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1582                         double advance = (glyph->advance.x / 64.0) / map->size;
1583                         //double mWidth = (glyph->metrics.width >> 6) / map->size;
1584                         //double mHeight = (glyph->metrics.height >> 6) / map->size;
1585
1586                         mapglyph->txmin = ( (double)(gC * map->glyphSize) + (double)(gpad_l - pad_l) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1587                         mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1588                         mapglyph->tymin = ( (double)(gR * map->glyphSize) + (double)(gpad_r - pad_r) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1589                         mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1590                         //mapglyph->vxmin = bearingX;
1591                         //mapglyph->vxmax = bearingX + mWidth;
1592                         mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
1593                         mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
1594                         //mapglyph->vymin = -bearingY;
1595                         //mapglyph->vymax = mHeight - bearingY;
1596                         mapglyph->vymin = (-glyph->bitmap_top - pad_t) / map->size;
1597                         mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
1598                         //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);
1599                         //mapglyph->advance_x = advance * usefont->size;
1600                         //mapglyph->advance_x = advance;
1601                         mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1602                         mapglyph->advance_y = 0;
1603
1604                         if (developer_font.integer)
1605                         {
1606                                 Con_DPrintf("glyphinfo:   Glyph: %lu   at (%i, %i)\n", (unsigned long)ch, gC, gR);
1607                                 Con_DPrintf("glyphinfo:   %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1608                                 if (ch >= 32 && ch <= 128)
1609                                         Con_DPrintf("glyphinfo:   Character: %c\n", (int)ch);
1610                                 Con_DPrintf("glyphinfo:   Vertex info:\n");
1611                                 Con_DPrintf("glyphinfo:     X: ( %f  --  %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1612                                 Con_DPrintf("glyphinfo:     Y: ( %f  --  %f )\n", mapglyph->vymin, mapglyph->vymax);
1613                                 Con_DPrintf("glyphinfo:   Texture info:\n");
1614                                 Con_DPrintf("glyphinfo:     S: ( %f  --  %f )\n", mapglyph->txmin, mapglyph->txmax);
1615                                 Con_DPrintf("glyphinfo:     T: ( %f  --  %f )\n", mapglyph->tymin, mapglyph->tymax);
1616                                 Con_DPrintf("glyphinfo:   Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1617                         }
1618                 }
1619                 map->glyphs[mapch].image = false;
1620         }
1621
1622         if (map->pic->tex == r_texture_notexture)
1623         {
1624                 int w = map->glyphSize * FONT_CHARS_PER_LINE;
1625                 int h = map->glyphSize * FONT_CHAR_LINES;
1626                 rtexture_t *tex;
1627                 // abuse the Draw_CachePic system to keep track of this texture
1628                 tex = R_LoadTexture2D(drawtexturepool, 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), -1, NULL);
1629                 // if tex is NULL for any reason, the pic->tex will remain set to r_texture_notexture
1630                 if (tex)
1631                         map->pic->tex = tex;
1632
1633                 if (r_font_diskcache.integer >= 1)
1634                 {
1635                         // swap to BGRA for tga writing...
1636                         int s = w * h;
1637                         int x;
1638                         int b;
1639                         for (x = 0;x < s;x++)
1640                         {
1641                                 b = data[x*4+0];
1642                                 data[x*4+0] = data[x*4+2];
1643                                 data[x*4+2] = b;
1644                         }
1645                         Image_WriteTGABGRA(va(vabuf, sizeof(vabuf), "%s.tga", map_identifier), w, h, data);
1646 #ifndef USE_GLES2
1647                         if (r_font_compress.integer && qglGetCompressedTexImageARB && tex)
1648                                 R_SaveTextureDDSFile(tex, va(vabuf, sizeof(vabuf), "dds/%s.dds", map_identifier), r_texture_dds_save.integer < 2, true);
1649 #endif
1650                 }
1651         }
1652
1653         if(data)
1654                 Mem_Free(data);
1655
1656         if (map->pic->tex == r_texture_notexture)
1657         {
1658                 // if the first try isn't successful, keep it with a broken texture
1659                 // otherwise we retry to load it every single frame where ft2 rendering is used
1660                 // this would be bad...
1661                 // only `data' must be freed
1662                 Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
1663                            font->name, mapstart->size, mapidx);
1664                 return false;
1665         }
1666         if (outmap)
1667                 *outmap = map;
1668         return true;
1669 }
1670
1671 qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
1672 {
1673         if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1674                 return false;
1675         // the first map must have been loaded already
1676         if (!font->font_maps[map_index])
1677                 return false;
1678         return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
1679 }
1680
1681 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1682 {
1683         while (start && start->start + FONT_CHARS_PER_MAP <= ch)
1684                 start = start->next;
1685         if (start && start->start > ch)
1686                 return NULL;
1687         return start;
1688 }