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