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