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