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