]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - ft2.c
on mac it's libfreetype.dylib and/or libfreetype.6.dylib
[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
10 static int img_fontmap[256] = {
11         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
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, // shift+digit line
14         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // digits
15         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // caps
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, // small
18         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small
19         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // specials
20         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // faces
21         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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 };
28
29 /*
30 ================================================================================
31 CVars introduced with the freetype extension
32 ================================================================================
33 */
34
35 cvar_t r_font_disable_freetype = {CVAR_SAVE, "r_font_disable_freetype", "1", "disable freetype support for fonts entirely"};
36 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"};
37 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!"};
38 cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"};
39 cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */};
40 cvar_t r_font_kerning = {CVAR_SAVE, "r_font_kerning", "1", "Use kerning if available"};
41 cvar_t developer_font = {CVAR_SAVE, "developer_font", "0", "prints debug messages about fonts"};
42
43 /*
44 ================================================================================
45 Function definitions. Taken from the freetype2 headers.
46 ================================================================================
47 */
48
49
50 FT_EXPORT( FT_Error )
51 (*qFT_Init_FreeType)( FT_Library  *alibrary );
52 FT_EXPORT( FT_Error )
53 (*qFT_Done_FreeType)( FT_Library  library );
54 /*
55 FT_EXPORT( FT_Error )
56 (*qFT_New_Face)( FT_Library   library,
57                  const char*  filepathname,
58                  FT_Long      face_index,
59                  FT_Face     *aface );
60 */
61 FT_EXPORT( FT_Error )
62 (*qFT_New_Memory_Face)( FT_Library      library,
63                         const FT_Byte*  file_base,
64                         FT_Long         file_size,
65                         FT_Long         face_index,
66                         FT_Face        *aface );
67 FT_EXPORT( FT_Error )
68 (*qFT_Done_Face)( FT_Face  face );
69 FT_EXPORT( FT_Error )
70 (*qFT_Select_Size)( FT_Face  face,
71                     FT_Int   strike_index );
72 FT_EXPORT( FT_Error )
73 (*qFT_Request_Size)( FT_Face          face,
74                      FT_Size_Request  req );
75 FT_EXPORT( FT_Error )
76 (*qFT_Set_Char_Size)( FT_Face     face,
77                       FT_F26Dot6  char_width,
78                       FT_F26Dot6  char_height,
79                       FT_UInt     horz_resolution,
80                       FT_UInt     vert_resolution );
81 FT_EXPORT( FT_Error )
82 (*qFT_Set_Pixel_Sizes)( FT_Face  face,
83                         FT_UInt  pixel_width,
84                         FT_UInt  pixel_height );
85 FT_EXPORT( FT_Error )
86 (*qFT_Load_Glyph)( FT_Face   face,
87                    FT_UInt   glyph_index,
88                    FT_Int32  load_flags );
89 FT_EXPORT( FT_Error )
90 (*qFT_Load_Char)( FT_Face   face,
91                   FT_ULong  char_code,
92                   FT_Int32  load_flags );
93 FT_EXPORT( FT_UInt )
94 (*qFT_Get_Char_Index)( FT_Face   face,
95                        FT_ULong  charcode );
96 FT_EXPORT( FT_Error )
97 (*qFT_Render_Glyph)( FT_GlyphSlot    slot,
98                      FT_Render_Mode  render_mode );
99 FT_EXPORT( FT_Error )
100 (*qFT_Get_Kerning)( FT_Face     face,
101                     FT_UInt     left_glyph,
102                     FT_UInt     right_glyph,
103                     FT_UInt     kern_mode,
104                     FT_Vector  *akerning );
105 FT_EXPORT( FT_Error )
106 (*qFT_Attach_Stream)( FT_Face        face,
107                       FT_Open_Args*  parameters );
108 /*
109 ================================================================================
110 Support for dynamically loading the FreeType2 library
111 ================================================================================
112 */
113
114 static dllfunction_t ft2funcs[] =
115 {
116         {"FT_Init_FreeType",            (void **) &qFT_Init_FreeType},
117         {"FT_Done_FreeType",            (void **) &qFT_Done_FreeType},
118         //{"FT_New_Face",                       (void **) &qFT_New_Face},
119         {"FT_New_Memory_Face",          (void **) &qFT_New_Memory_Face},
120         {"FT_Done_Face",                (void **) &qFT_Done_Face},
121         {"FT_Select_Size",              (void **) &qFT_Select_Size},
122         {"FT_Request_Size",             (void **) &qFT_Request_Size},
123         {"FT_Set_Char_Size",            (void **) &qFT_Set_Char_Size},
124         {"FT_Set_Pixel_Sizes",          (void **) &qFT_Set_Pixel_Sizes},
125         {"FT_Load_Glyph",               (void **) &qFT_Load_Glyph},
126         {"FT_Load_Char",                (void **) &qFT_Load_Char},
127         {"FT_Get_Char_Index",           (void **) &qFT_Get_Char_Index},
128         {"FT_Render_Glyph",             (void **) &qFT_Render_Glyph},
129         {"FT_Get_Kerning",              (void **) &qFT_Get_Kerning},
130         {"FT_Attach_Stream",            (void **) &qFT_Attach_Stream},
131         {NULL, NULL}
132 };
133
134 /// Handle for FreeType2 DLL
135 static dllhandle_t ft2_dll = NULL;
136
137 /// Memory pool for fonts
138 static mempool_t *font_mempool= NULL;
139 static rtexturepool_t *font_texturepool = NULL;
140
141 /// FreeType library handle
142 static FT_Library font_ft2lib = NULL;
143
144 /*
145 ====================
146 Font_CloseLibrary
147
148 Unload the FreeType2 DLL
149 ====================
150 */
151 void Font_CloseLibrary (void)
152 {
153         if (font_mempool)
154                 Mem_FreePool(&font_mempool);
155         if (font_texturepool)
156                 R_FreeTexturePool(&font_texturepool);
157         if (font_ft2lib && qFT_Done_FreeType)
158         {
159                 qFT_Done_FreeType(font_ft2lib);
160                 font_ft2lib = NULL;
161         }
162         Sys_UnloadLibrary (&ft2_dll);
163 }
164
165 /*
166 ====================
167 Font_OpenLibrary
168
169 Try to load the FreeType2 DLL
170 ====================
171 */
172 qboolean Font_OpenLibrary (void)
173 {
174         const char* dllnames [] =
175         {
176 #if defined(WIN32)
177                 "freetype6.dll",
178                 "libfreetype-6.dll",
179 #elif defined(MACOSX)
180                 "libfreetype.6.dylib",
181                 "libfreetype.dylib",
182 #else
183                 "libfreetype.so.6",
184                 "libfreetype.so",
185 #endif
186                 NULL
187         };
188
189         if (r_font_disable_freetype.integer)
190                 return false;
191
192         // Already loaded?
193         if (ft2_dll)
194                 return true;
195
196         // Load the DLL
197         if (!Sys_LoadLibrary (dllnames, &ft2_dll, ft2funcs))
198                 return false;
199         return true;
200 }
201
202 /*
203 ====================
204 Font_Init
205
206 Initialize the freetype2 font subsystem
207 ====================
208 */
209
210 void font_start(void)
211 {
212         if (!Font_OpenLibrary())
213                 return;
214
215         if (qFT_Init_FreeType(&font_ft2lib))
216         {
217                 Con_Print("ERROR: Failed to initialize the FreeType2 library!\n");
218                 Font_CloseLibrary();
219                 return;
220         }
221
222         font_mempool = Mem_AllocPool("FONT", 0, NULL);
223         if (!font_mempool)
224         {
225                 Con_Print("ERROR: Failed to allocate FONT memory pool!\n");
226                 Font_CloseLibrary();
227                 return;
228         }
229
230         font_texturepool = R_AllocTexturePool();
231         if (!font_texturepool)
232         {
233                 Con_Print("ERROR: Failed to allocate FONT texture pool!\n");
234                 Font_CloseLibrary();
235                 return;
236         }
237 }
238
239 void font_shutdown(void)
240 {
241         int i;
242         for (i = 0; i < MAX_FONTS; ++i)
243         {
244                 if (dp_fonts[i].ft2)
245                 {
246                         Font_UnloadFont(dp_fonts[i].ft2);
247                         dp_fonts[i].ft2 = NULL;
248                 }
249         }
250         Font_CloseLibrary();
251 }
252
253 void font_newmap(void)
254 {
255 }
256
257 void Font_Init(void)
258 {
259         Cvar_RegisterVariable(&r_font_disable_freetype);
260         Cvar_RegisterVariable(&r_font_use_alpha_textures);
261         Cvar_RegisterVariable(&r_font_size_snapping);
262         Cvar_RegisterVariable(&r_font_hinting);
263         Cvar_RegisterVariable(&r_font_antialias);
264         Cvar_RegisterVariable(&r_font_kerning);
265         Cvar_RegisterVariable(&developer_font);
266 }
267
268 /*
269 ================================================================================
270 Implementation of a more or less lazy font loading and rendering code.
271 ================================================================================
272 */
273
274 #include "ft2_fontdefs.h"
275
276 ft2_font_t *Font_Alloc(void)
277 {
278         if (!ft2_dll)
279                 return NULL;
280         return Mem_Alloc(font_mempool, sizeof(ft2_font_t));
281 }
282
283 qboolean Font_Attach(ft2_font_t *font, ft2_attachment_t *attachment)
284 {
285         ft2_attachment_t *na;
286
287         font->attachmentcount++;
288         na = (ft2_attachment_t*)Mem_Alloc(font_mempool, sizeof(font->attachments[0]) * font->attachmentcount);
289         if (na == NULL)
290                 return false;
291         if (font->attachments && font->attachmentcount > 1)
292         {
293                 memcpy(na, font->attachments, sizeof(font->attachments[0]) * (font->attachmentcount - 1));
294                 Mem_Free(font->attachments);
295         }
296         memcpy(na + sizeof(font->attachments[0]) * (font->attachmentcount - 1), attachment, sizeof(*attachment));
297         font->attachments = na;
298         return true;
299 }
300
301 static float Font_VirtualToRealSize(float sz)
302 {
303         int vh, vw, si;
304         float sn;
305         if(sz < 0)
306                 return sz;
307         vw = ((vid.width > 0) ? vid.width : vid_width.value);
308         vh = ((vid.height > 0) ? vid.height : vid_height.value);
309         // now try to scale to our actual size:
310         sn = sz * vh / vid_conheight.value;
311         si = (int)sn;
312         if ( sn - (float)si >= 0.5 )
313                 ++si;
314         return si;
315 }
316
317 static float Font_SnapTo(float val, float snapwidth)
318 {
319         return floor(val / snapwidth + 0.5f) * snapwidth;
320 }
321
322 static qboolean Font_LoadFile(const char *name, int _face, ft2_font_t *font);
323 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean no_texture, qboolean no_kerning);
324 qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt)
325 {
326         int s, count, i;
327         ft2_font_t *ft2, *fbfont, *fb;
328
329         ft2 = Font_Alloc();
330         if (!ft2)
331         {
332                 dpfnt->ft2 = NULL;
333                 return false;
334         }
335
336         // check if a fallback font has been specified, if it has been, and the
337         // font fails to load, use the image font as main font
338         for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
339         {
340                 if (dpfnt->fallbacks[i][0])
341                         break;
342         }
343
344         if (!Font_LoadFile(name, dpfnt->req_face, ft2))
345         {
346                 if (i >= MAX_FONT_FALLBACKS)
347                 {
348                         dpfnt->ft2 = NULL;
349                         Mem_Free(ft2);
350                         return false;
351                 }
352                 strlcpy(ft2->name, name, sizeof(ft2->name));
353                 ft2->image_font = true;
354                 ft2->has_kerning = false;
355         }
356         else
357         {
358                 ft2->image_font = false;
359         }
360
361         // attempt to load fallback fonts:
362         fbfont = ft2;
363         for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
364         {
365                 if (!dpfnt->fallbacks[i][0])
366                         break;
367                 if (! (fb = Font_Alloc()) )
368                 {
369                         Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
370                         break;
371                 }
372                 if (!Font_LoadFile(dpfnt->fallbacks[i], dpfnt->fallback_faces[i], fb))
373                 {
374                         Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
375                         Mem_Free(fb);
376                         break;
377                 }
378                 count = 0;
379                 for (s = 0; s < MAX_FONT_SIZES; ++s)
380                 {
381                         if (Font_LoadSize(fb, Font_VirtualToRealSize(dpfnt->req_sizes[s]), true, false))
382                                 ++count;
383                 }
384                 if (!count)
385                 {
386                         Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
387                         Font_UnloadFont(fb);
388                         Mem_Free(fb);
389                         break;
390                 }
391                 // at least one size of the fallback font loaded successfully
392                 // link it:
393                 fbfont->next = fb;
394                 fbfont = fb;
395         }
396
397         if (fbfont == ft2 && ft2->image_font)
398         {
399                 // no fallbacks were loaded successfully:
400                 dpfnt->ft2 = NULL;
401                 Mem_Free(ft2);
402                 return false;
403         }
404
405         count = 0;
406         for (s = 0; s < MAX_FONT_SIZES; ++s)
407         {
408                 if (Font_LoadSize(ft2, Font_VirtualToRealSize(dpfnt->req_sizes[s]), false, false))
409                         ++count;
410         }
411         if (!count)
412         {
413                 // loading failed for every requested size
414                 Font_UnloadFont(ft2);
415                 Mem_Free(ft2);
416                 dpfnt->ft2 = NULL;
417                 return false;
418         }
419
420         //Con_Printf("%i sizes loaded\n", count);
421         dpfnt->ft2 = ft2;
422         return true;
423 }
424
425 static qboolean Font_LoadFile(const char *name, int _face, ft2_font_t *font)
426 {
427         size_t namelen;
428         char filename[MAX_QPATH];
429         int status;
430         size_t i;
431         unsigned char *data;
432         fs_offset_t datasize;
433
434         memset(font, 0, sizeof(*font));
435
436         if (!Font_OpenLibrary())
437         {
438                 if (!r_font_disable_freetype.integer)
439                 {
440                         Con_Printf("WARNING: can't open load font %s\n"
441                                    "You need the FreeType2 DLL to load font files\n",
442                                    name);
443                 }
444                 return false;
445         }
446
447         namelen = strlen(name);
448
449         memcpy(filename, name, namelen);
450         memcpy(filename + namelen, ".ttf", 5);
451         data = FS_LoadFile(filename, font_mempool, false, &datasize);
452         if (!data)
453         {
454                 memcpy(filename + namelen, ".otf", 5);
455                 data = FS_LoadFile(filename, font_mempool, false, &datasize);
456         }
457         if (!data)
458         {
459                 ft2_attachment_t afm;
460
461                 memcpy(filename + namelen, ".pfb", 5);
462                 data = FS_LoadFile(filename, font_mempool, false, &datasize);
463
464                 if (data)
465                 {
466                         memcpy(filename + namelen, ".afm", 5);
467                         afm.data = FS_LoadFile(filename, font_mempool, false, &afm.size);
468
469                         if (afm.data)
470                                 Font_Attach(font, &afm);
471                 }
472         }
473
474         if (!data)
475         {
476                 // FS_LoadFile being not-quiet should print an error :)
477                 return false;
478         }
479         Con_Printf("Loading font %s face %i...\n", filename, _face);
480
481         status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
482         if (status && _face != 0)
483         {
484                 Con_Printf("Failed to load face %i of %s. Falling back to face 0\n", _face, name);
485                 _face = 0;
486                 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, 0, (FT_Face*)&font->face);
487         }
488         if (status)
489         {
490                 Con_Printf("ERROR: can't create face for %s\n"
491                            "Error %i\n", // TODO: error strings
492                            name, status);
493                 Font_UnloadFont(font);
494                 return false;
495         }
496
497         // add the attachments
498         for (i = 0; i < font->attachmentcount; ++i)
499         {
500                 FT_Open_Args args;
501                 memset(&args, 0, sizeof(args));
502                 args.flags = FT_OPEN_MEMORY;
503                 args.memory_base = (const FT_Byte*)font->attachments[i].data;
504                 args.memory_size = font->attachments[i].size;
505                 if (qFT_Attach_Stream(font->face, &args))
506                         Con_Printf("Failed to add attachment %u to %s\n", (unsigned)i, font->name);
507         }
508
509         memcpy(font->name, name, namelen+1);
510         font->image_font = false;
511         font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
512         return true;
513 }
514
515 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
516 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean no_texture, qboolean no_kerning)
517 {
518         int map_index;
519         ft2_font_map_t *fmap, temp;
520
521         if (!(size > 0.001f && size < 1000.0f))
522                 size = 0;
523
524         if (!size)
525                 size = 16;
526         if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
527                 return false;
528
529         if (!no_texture)
530         {
531                 for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
532                 {
533                         if (!font->font_maps[map_index])
534                                 break;
535                         // if a similar size has already been loaded, ignore this one
536                         //abs(font->font_maps[map_index]->size - size) < 4
537                         if (font->font_maps[map_index]->size == size)
538                                 return true;
539                 }
540
541                 if (map_index >= MAX_FONT_SIZES)
542                         return false;
543
544                 memset(&temp, 0, sizeof(temp));
545                 temp.size = size;
546                 temp.glyphSize = CeilPowerOf2(size*2);
547                 temp.sfx = (1.0/64.0)/(double)size;
548                 temp.sfy = (1.0/64.0)/(double)size;
549                 temp.intSize = -1; // negative value: LoadMap must search now :)
550                 if (!Font_LoadMap(font, &temp, 0, &fmap))
551                 {
552                         Con_Printf("ERROR: can't load the first character map for %s\n"
553                                    "This is fatal\n",
554                                    font->name);
555                         Font_UnloadFont(font);
556                         return false;
557                 }
558                 font->font_maps[map_index] = temp.next;
559
560                 fmap->sfx = temp.sfx;
561                 fmap->sfy = temp.sfy;
562         }
563         if (!no_kerning)
564         {
565                 // load the default kerning vector:
566                 if (font->has_kerning)
567                 {
568                         Uchar l, r;
569                         FT_Vector kernvec;
570                         for (l = 0; l < 256; ++l)
571                         {
572                                 for (r = 0; r < 256; ++r)
573                                 {
574                                         FT_ULong ul, ur;
575                                         ul = qFT_Get_Char_Index(font->face, l);
576                                         ur = qFT_Get_Char_Index(font->face, r);
577                                         if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
578                                         {
579                                                 fmap->kerning.kerning[l][r][0] = 0;
580                                                 fmap->kerning.kerning[l][r][1] = 0;
581                                         }
582                                         else
583                                         {
584                                                 fmap->kerning.kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
585                                                 fmap->kerning.kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
586                                         }
587                                 }
588                         }
589                 }
590         }
591
592         return true;
593 }
594
595 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
596 {
597         int match = -1;
598         int value = 1000000;
599         int nval;
600         int matchsize = -10000;
601         int m;
602         float fsize_x, fsize_y;
603         ft2_font_map_t **maps = font->font_maps;
604
605         fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value;
606         if(outw && *outw)
607                 fsize_x = *outw * vid.width / vid_conwidth.value;
608         if(outh && *outh)
609                 fsize_y = *outh * vid.height / vid_conheight.value;
610
611         if (fsize_x < 0)
612         {
613                 if(fsize_y < 0)
614                         fsize_x = fsize_y = 16;
615                 else
616                         fsize_x = fsize_y;
617         }
618         else
619         {
620                 if(fsize_y < 0)
621                         fsize_y = fsize_x;
622         }
623
624         for (m = 0; m < MAX_FONT_SIZES; ++m)
625         {
626                 if (!maps[m])
627                         continue;
628                 // "round up" to the bigger size if two equally-valued matches exist
629                 nval = 0.5 * (abs(maps[m]->size - fsize_x) + abs(maps[m]->size - fsize_y));
630                 if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
631                 {
632                         value = nval;
633                         match = m;
634                         matchsize = maps[m]->size;
635                         if (value == 0) // there is no better match
636                                 break;
637                 }
638         }
639         if (value <= r_font_size_snapping.value)
640         {
641                 // do NOT keep the aspect for perfect rendering
642                 if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
643                 if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width;
644         }
645         return match;
646 }
647
648 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
649 {
650         if (index < 0 || index >= MAX_FONT_SIZES)
651                 return NULL;
652         return font->font_maps[index];
653 }
654
655 static qboolean Font_SetSize(ft2_font_t *font, float w, float h)
656 {
657         if (font->currenth == h &&
658             ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
659              font->currentw == w)) // same size has been requested
660         {
661                 return true;
662         }
663         // sorry, but freetype doesn't seem to care about other sizes
664         w = (int)w;
665         h = (int)h;
666         if (font->image_font)
667         {
668                 if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
669                         return false;
670         }
671         else
672         {
673                 if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
674                         return false;
675         }
676         font->currentw = w;
677         font->currenth = h;
678         return true;
679 }
680
681 qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
682 {
683         ft2_font_map_t *fmap;
684         if (!font->has_kerning || !r_font_kerning.integer)
685                 return false;
686         if (map_index < 0 || map_index >= MAX_FONT_SIZES)
687                 return false;
688         fmap = font->font_maps[map_index];
689         if (!fmap)
690                 return false;
691         if (left < 256 && right < 256)
692         {
693                 //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
694                 // quick-kerning, be aware of the size: scale it
695                 if (outx) *outx = fmap->kerning.kerning[left][right][0];// * (w / (float)fmap->size);
696                 if (outy) *outy = fmap->kerning.kerning[left][right][1];// * (h / (float)fmap->size);
697                 return true;
698         }
699         else
700         {
701                 FT_Vector kernvec;
702                 FT_ULong ul, ur;
703
704                 //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
705 #if 0
706                 if (!Font_SetSize(font, w, h))
707                 {
708                         // this deserves an error message
709                         Con_Printf("Failed to get kerning for %s\n", font->name);
710                         return false;
711                 }
712                 ul = qFT_Get_Char_Index(font->face, left);
713                 ur = qFT_Get_Char_Index(font->face, right);
714                 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
715                 {
716                         if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);
717                         if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);
718                         return true;
719                 }
720 #endif
721                 if (!Font_SetSize(font, fmap->intSize, fmap->intSize))
722                 {
723                         // this deserves an error message
724                         Con_Printf("Failed to get kerning for %s\n", font->name);
725                         return false;
726                 }
727                 ul = qFT_Get_Char_Index(font->face, left);
728                 ur = qFT_Get_Char_Index(font->face, right);
729                 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
730                 {
731                         if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);// * (w / (float)fmap->size);
732                         if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);// * (h / (float)fmap->size);
733                         return true;
734                 }
735                 return false;
736         }
737 }
738
739 qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
740 {
741         return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
742 }
743
744 static void UnloadMapRec(ft2_font_map_t *map)
745 {
746         if (map->texture)
747         {
748                 R_FreeTexture(map->texture);
749                 map->texture = NULL;
750         }
751         if (map->next)
752                 UnloadMapRec(map->next);
753         Mem_Free(map);
754 }
755
756 void Font_UnloadFont(ft2_font_t *font)
757 {
758         int i;
759         if (font->attachments && font->attachmentcount)
760         {
761                 Mem_Free(font->attachments);
762                 font->attachmentcount = 0;
763                 font->attachments = NULL;
764         }
765         for (i = 0; i < MAX_FONT_SIZES; ++i)
766         {
767                 if (font->font_maps[i])
768                 {
769                         UnloadMapRec(font->font_maps[i]);
770                         font->font_maps[i] = NULL;
771                 }
772         }
773         if (ft2_dll)
774         {
775                 if (font->face)
776                 {
777                         qFT_Done_Face((FT_Face)font->face);
778                         font->face = NULL;
779                 }
780         }
781 }
782
783 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
784 {
785         char map_identifier[MAX_QPATH];
786         unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
787         unsigned char *data;
788         FT_ULong ch, mapch;
789         int status;
790         int tp;
791         FT_Int32 load_flags;
792
793         int pitch;
794         int gR, gC; // glyph position: row and column
795
796         ft2_font_map_t *map, *next;
797         ft2_font_t *usefont;
798
799         FT_Face fontface;
800
801         int bytesPerPixel = 4; // change the conversion loop too if you change this!
802
803         if (outmap)
804                 *outmap = NULL;
805
806         if (r_font_use_alpha_textures.integer)
807                 bytesPerPixel = 1;
808
809         if (font->image_font)
810                 fontface = (FT_Face)font->next->face;
811         else
812                 fontface = (FT_Face)font->face;
813
814         switch(r_font_antialias.integer)
815         {
816                 case 0:
817                         switch(r_font_hinting.integer)
818                         {
819                                 case 0:
820                                         load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
821                                         break;
822                                 case 1:
823                                 case 2:
824                                         load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
825                                         break;
826                                 default:
827                                 case 3:
828                                         load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
829                                         break;
830                         }
831                         break;
832                 default:
833                 case 1:
834                         switch(r_font_hinting.integer)
835                         {
836                                 case 0:
837                                         load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL;
838                                         break;
839                                 case 1:
840                                         load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
841                                         break;
842                                 case 2:
843                                         load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
844                                         break;
845                                 default:
846                                 case 3:
847                                         load_flags = FT_LOAD_TARGET_NORMAL;
848                                         break;
849                         }
850                         break;
851         }
852
853         //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
854         //if (status)
855         if (font->image_font && mapstart->intSize < 0)
856                 mapstart->intSize = mapstart->size;
857         if (mapstart->intSize < 0)
858         {
859                 mapstart->intSize = mapstart->size;
860                 while (1)
861                 {
862                         if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
863                         {
864                                 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
865                                 return false;
866                         }
867                         if ((fontface->size->metrics.height>>6) <= mapstart->size)
868                                 break;
869                         if (mapstart->intSize < 2)
870                         {
871                                 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
872                                 return false;
873                         }
874                         --mapstart->intSize;
875                 }
876                 Con_DPrintf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
877         }
878
879         if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
880         {
881                 Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
882                 return false;
883         }
884
885         map = Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
886         if (!map)
887         {
888                 Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
889                 return false;
890         }
891
892         // copy over the information
893         map->size = mapstart->size;
894         map->intSize = mapstart->intSize;
895         map->glyphSize = mapstart->glyphSize;
896         map->sfx = mapstart->sfx;
897         map->sfy = mapstart->sfy;
898
899         pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
900         data = Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
901         if (!data)
902         {
903                 Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
904                 Mem_Free(map);
905                 return false;
906         }
907         memset(map->width_of, 0, sizeof(map->width_of));
908
909         // initialize as white texture with zero alpha
910         tp = 0;
911         while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
912         {
913                 if (bytesPerPixel == 4)
914                 {
915                         data[tp++] = 0xFF;
916                         data[tp++] = 0xFF;
917                         data[tp++] = 0xFF;
918                 }
919                 data[tp++] = 0x00;
920         }
921
922         // insert the map
923         map->start = mapidx * FONT_CHARS_PER_MAP;
924         next = mapstart;
925         while(next->next && next->next->start < map->start)
926                 next = next->next;
927         map->next = next->next;
928         next->next = map;
929
930         gR = 0;
931         gC = -1;
932         for (ch = map->start;
933              ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
934              ++ch)
935         {
936                 FT_ULong glyphIndex;
937                 int w, h, x, y;
938                 FT_GlyphSlot glyph;
939                 FT_Bitmap *bmp;
940                 unsigned char *imagedata, *dst, *src;
941                 glyph_slot_t *mapglyph;
942                 FT_Face face;
943
944                 mapch = ch - map->start;
945
946                 if (developer_font.integer)
947                         Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
948
949                 ++gC;
950                 if (gC >= FONT_CHARS_PER_LINE)
951                 {
952                         gC -= FONT_CHARS_PER_LINE;
953                         ++gR;
954                 }
955
956                 imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
957                 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
958                 // we need the glyphIndex
959                 face = font->face;
960                 usefont = NULL;
961                 if (font->image_font && mapch == ch && img_fontmap[mapch])
962                 {
963                         map->glyphs[mapch].image = true;
964                         continue;
965                 }
966                 glyphIndex = qFT_Get_Char_Index(face, ch);
967                 if (glyphIndex == 0)
968                 {
969                         // by convention, 0 is the "missing-glyph"-glyph
970                         // try to load from a fallback font
971                         for(usefont = font->next; usefont != NULL; usefont = usefont->next)
972                         {
973                                 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
974                                         continue;
975                                 // try that glyph
976                                 face = usefont->face;
977                                 glyphIndex = qFT_Get_Char_Index(face, ch);
978                                 if (glyphIndex == 0)
979                                         continue;
980                                 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
981                                 if (status)
982                                         continue;
983                                 break;
984                         }
985                         if (!usefont)
986                         {
987                                 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
988                                 // now we let it use the "missing-glyph"-glyph
989                                 face = font->face;
990                                 glyphIndex = 0;
991                         }
992                 }
993
994                 if (!usefont)
995                 {
996                         usefont = font;
997                         face = font->face;
998                         status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
999                         if (status)
1000                         {
1001                                 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1002                                 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1003                                 continue;
1004                         }
1005                 }
1006
1007                 glyph = face->glyph;
1008                 bmp = &glyph->bitmap;
1009
1010                 w = bmp->width;
1011                 h = bmp->rows;
1012
1013                 if (w > map->glyphSize || h > map->glyphSize) {
1014                         Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
1015                         if (w > map->glyphSize)
1016                                 w = map->glyphSize;
1017                         if (h > map->glyphSize)
1018                                 h = map->glyphSize;
1019                 }
1020
1021                 switch (bmp->pixel_mode)
1022                 {
1023                 case FT_PIXEL_MODE_MONO:
1024                         if (developer_font.integer)
1025                                 Con_DPrint("glyphinfo:   Pixel Mode: MONO\n");
1026                         break;
1027                 case FT_PIXEL_MODE_GRAY2:
1028                         if (developer_font.integer)
1029                                 Con_DPrint("glyphinfo:   Pixel Mode: GRAY2\n");
1030                         break;
1031                 case FT_PIXEL_MODE_GRAY4:
1032                         if (developer_font.integer)
1033                                 Con_DPrint("glyphinfo:   Pixel Mode: GRAY4\n");
1034                         break;
1035                 case FT_PIXEL_MODE_GRAY:
1036                         if (developer_font.integer)
1037                                 Con_DPrint("glyphinfo:   Pixel Mode: GRAY\n");
1038                         break;
1039                 default:
1040                         if (developer_font.integer)
1041                                 Con_DPrintf("glyphinfo:   Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1042                         Mem_Free(data);
1043                         Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1044                         return false;
1045                 }
1046                 for (y = 0; y < h; ++y)
1047                 {
1048                         dst = imagedata + y * pitch;
1049                         src = bmp->buffer + y * bmp->pitch;
1050
1051                         switch (bmp->pixel_mode)
1052                         {
1053                         case FT_PIXEL_MODE_MONO:
1054                                 dst += bytesPerPixel - 1; // shift to alpha byte
1055                                 for (x = 0; x < bmp->width; x += 8)
1056                                 {
1057                                         unsigned char ch = *src++;
1058                                         *dst = 255 * !!((ch & 0x80) >> 7); dst += bytesPerPixel;
1059                                         *dst = 255 * !!((ch & 0x40) >> 6); dst += bytesPerPixel;
1060                                         *dst = 255 * !!((ch & 0x20) >> 5); dst += bytesPerPixel;
1061                                         *dst = 255 * !!((ch & 0x10) >> 4); dst += bytesPerPixel;
1062                                         *dst = 255 * !!((ch & 0x08) >> 3); dst += bytesPerPixel;
1063                                         *dst = 255 * !!((ch & 0x04) >> 2); dst += bytesPerPixel;
1064                                         *dst = 255 * !!((ch & 0x02) >> 1); dst += bytesPerPixel;
1065                                         *dst = 255 * !!((ch & 0x01) >> 0); dst += bytesPerPixel;
1066                                 }
1067                                 break;
1068                         case FT_PIXEL_MODE_GRAY2:
1069                                 dst += bytesPerPixel - 1; // shift to alpha byte
1070                                 for (x = 0; x < bmp->width; x += 4)
1071                                 {
1072                                         unsigned char ch = *src++;
1073                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1074                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1075                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1076                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1077                                 }
1078                                 break;
1079                         case FT_PIXEL_MODE_GRAY4:
1080                                 dst += bytesPerPixel - 1; // shift to alpha byte
1081                                 for (x = 0; x < bmp->width; x += 2)
1082                                 {
1083                                         unsigned char ch = *src++;
1084                                         *dst = ( ((ch & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
1085                                         *dst = ( ((ch & 0x0F) ) * 0x11); dst += bytesPerPixel;
1086                                 }
1087                                 break;
1088                         case FT_PIXEL_MODE_GRAY:
1089                                 // in this case pitch should equal width
1090                                 for (tp = 0; tp < bmp->pitch; ++tp)
1091                                         dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
1092
1093                                 //memcpy((void*)dst, (void*)src, bmp->pitch);
1094                                 //dst += bmp->pitch;
1095                                 break;
1096                         default:
1097                                 break;
1098                         }
1099                 }
1100
1101                 // now fill map->glyphs[ch - map->start]
1102                 mapglyph = &map->glyphs[mapch];
1103
1104                 {
1105                         // old way
1106                         // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1107
1108                         double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1109                         //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1110                         double advance = (glyph->advance.x / 64.0) / map->size;
1111                         //double mWidth = (glyph->metrics.width >> 6) / map->size;
1112                         //double mHeight = (glyph->metrics.height >> 6) / map->size;
1113
1114                         mapglyph->txmin = ( (double)(gC * map->glyphSize) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1115                         mapglyph->txmax = mapglyph->txmin + (double)bmp->width / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1116                         mapglyph->tymin = ( (double)(gR * map->glyphSize) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1117                         mapglyph->tymax = mapglyph->tymin + (double)bmp->rows / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1118                         //mapglyph->vxmin = bearingX;
1119                         //mapglyph->vxmax = bearingX + mWidth;
1120                         mapglyph->vxmin = glyph->bitmap_left / map->size;
1121                         mapglyph->vxmax = mapglyph->vxmin + bmp->width / map->size; // don't ask
1122                         //mapglyph->vymin = -bearingY;
1123                         //mapglyph->vymax = mHeight - bearingY;
1124                         mapglyph->vymin = -glyph->bitmap_top / map->size;
1125                         mapglyph->vymax = mapglyph->vymin + bmp->rows / map->size;
1126                         //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);
1127                         //mapglyph->advance_x = advance * usefont->size;
1128                         //mapglyph->advance_x = advance;
1129                         mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1130                         mapglyph->advance_y = 0;
1131
1132                         if (developer_font.integer)
1133                         {
1134                                 Con_DPrintf("glyphinfo:   Glyph: %lu   at (%i, %i)\n", (unsigned long)ch, gC, gR);
1135                                 Con_DPrintf("glyphinfo:   %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1136                                 if (ch >= 32 && ch <= 128)
1137                                         Con_DPrintf("glyphinfo:   Character: %c\n", (int)ch);
1138                                 Con_DPrintf("glyphinfo:   Vertex info:\n");
1139                                 Con_DPrintf("glyphinfo:     X: ( %f  --  %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1140                                 Con_DPrintf("glyphinfo:     Y: ( %f  --  %f )\n", mapglyph->vymin, mapglyph->vymax);
1141                                 Con_DPrintf("glyphinfo:   Texture info:\n");
1142                                 Con_DPrintf("glyphinfo:     S: ( %f  --  %f )\n", mapglyph->txmin, mapglyph->txmax);
1143                                 Con_DPrintf("glyphinfo:     T: ( %f  --  %f )\n", mapglyph->tymin, mapglyph->tymax);
1144                                 Con_DPrintf("glyphinfo:   Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1145                         }
1146                 }
1147                 map->glyphs[mapch].image = false;
1148         }
1149
1150         // create a texture from the data now
1151
1152         if (developer_font.integer > 100)
1153         {
1154                 // LordHavoc: why are we writing this?  And why not write it as TGA using the appropriate function?
1155                 // view using `display -depth 8 -size 512x512 name_page.rgba` (be sure to use a correct -size parameter)
1156                 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u.rgba", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1157                 FS_WriteFile(map_identifier, data, pitch * FONT_CHAR_LINES * map->glyphSize);
1158         }
1159         dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1160
1161         // probably use bytesPerPixel here instead?
1162         if (r_font_use_alpha_textures.integer)
1163         {
1164                 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1165                                                map->glyphSize * FONT_CHARS_PER_LINE,
1166                                                map->glyphSize * FONT_CHAR_LINES,
1167                                                data, TEXTYPE_ALPHA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL);
1168         } else {
1169                 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1170                                                map->glyphSize * FONT_CHARS_PER_LINE,
1171                                                map->glyphSize * FONT_CHAR_LINES,
1172                                                data, TEXTYPE_RGBA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL);
1173         }
1174
1175         Mem_Free(data);
1176         if (!map->texture)
1177         {
1178                 // if the first try isn't successful, keep it with a broken texture
1179                 // otherwise we retry to load it every single frame where ft2 rendering is used
1180                 // this would be bad...
1181                 // only `data' must be freed
1182                 Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
1183                            font->name, mapstart->size, mapidx);
1184                 return false;
1185         }
1186         if (outmap)
1187                 *outmap = map;
1188         return true;
1189 }
1190
1191 qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
1192 {
1193         if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1194                 return false;
1195         // the first map must have been loaded already
1196         if (!font->font_maps[map_index])
1197                 return false;
1198         return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
1199 }
1200
1201 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1202 {
1203         while (start && start->start + FONT_CHARS_PER_MAP < ch)
1204                 start = start->next;
1205         if (start && start->start > ch)
1206                 return NULL;
1207         return start;
1208 }