]> icculus.org git repositories - divverent/darkplaces.git/blob - ft2.c
add a missing parameter to that function :P
[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 check_only);
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))
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))
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 void Font_Postprocess(unsigned char *imagedata, int pitch, int bpp, int w, int h, int *pad_l, int *pad_r, int *pad_t, int *pad_b)
518 {
519         if(imagedata)
520         {
521                 // perform operation, not exceeding the passed padding values,
522                 // but possibly reducing them
523         }
524         else
525         {
526                 // calculate parameters
527                 *pad_l = *pad_r = *pad_t = *pad_b = 0;
528         }
529 }
530
531 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size);
532 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
533 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only)
534 {
535         int map_index;
536         ft2_font_map_t *fmap, temp;
537         int gpad_l, gpad_r, gpad_t, gpad_b;
538
539         if (!(size > 0.001f && size < 1000.0f))
540                 size = 0;
541
542         if (!size)
543                 size = 16;
544         if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
545                 return false;
546
547         for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
548         {
549                 if (!font->font_maps[map_index])
550                         break;
551                 // if a similar size has already been loaded, ignore this one
552                 //abs(font->font_maps[map_index]->size - size) < 4
553                 if (font->font_maps[map_index]->size == size)
554                         return true;
555         }
556
557         if (map_index >= MAX_FONT_SIZES)
558                 return false;
559
560         if (check_only) {
561                 FT_Face fontface;
562                 if (font->image_font)
563                         fontface = (FT_Face)font->next->face;
564                 else
565                         fontface = (FT_Face)font->face;
566                 return (Font_SearchSize(font, fontface, size) > 0);
567         }
568
569         Font_Postprocess(NULL, 0, 0, 0, 0, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
570
571         memset(&temp, 0, sizeof(temp));
572         temp.size = size;
573         temp.glyphSize = CeilPowerOf2(size*2 + max(gpad_l + gpad_r, gpad_t + gpad_b));
574         temp.sfx = (1.0/64.0)/(double)size;
575         temp.sfy = (1.0/64.0)/(double)size;
576         temp.intSize = -1; // negative value: LoadMap must search now :)
577         if (!Font_LoadMap(font, &temp, 0, &fmap))
578         {
579                 Con_Printf("ERROR: can't load the first character map for %s\n"
580                            "This is fatal\n",
581                            font->name);
582                 Font_UnloadFont(font);
583                 return false;
584         }
585         font->font_maps[map_index] = temp.next;
586
587         fmap->sfx = temp.sfx;
588         fmap->sfy = temp.sfy;
589
590         // load the default kerning vector:
591         if (font->has_kerning)
592         {
593                 Uchar l, r;
594                 FT_Vector kernvec;
595                 for (l = 0; l < 256; ++l)
596                 {
597                         for (r = 0; r < 256; ++r)
598                         {
599                                 FT_ULong ul, ur;
600                                 ul = qFT_Get_Char_Index(font->face, l);
601                                 ur = qFT_Get_Char_Index(font->face, r);
602                                 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
603                                 {
604                                         fmap->kerning.kerning[l][r][0] = 0;
605                                         fmap->kerning.kerning[l][r][1] = 0;
606                                 }
607                                 else
608                                 {
609                                         fmap->kerning.kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
610                                         fmap->kerning.kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
611                                 }
612                         }
613                 }
614         }
615         return true;
616 }
617
618 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
619 {
620         int match = -1;
621         int value = 1000000;
622         int nval;
623         int matchsize = -10000;
624         int m;
625         float fsize_x, fsize_y;
626         ft2_font_map_t **maps = font->font_maps;
627
628         fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value;
629         if(outw && *outw)
630                 fsize_x = *outw * vid.width / vid_conwidth.value;
631         if(outh && *outh)
632                 fsize_y = *outh * vid.height / vid_conheight.value;
633
634         if (fsize_x < 0)
635         {
636                 if(fsize_y < 0)
637                         fsize_x = fsize_y = 16;
638                 else
639                         fsize_x = fsize_y;
640         }
641         else
642         {
643                 if(fsize_y < 0)
644                         fsize_y = fsize_x;
645         }
646
647         for (m = 0; m < MAX_FONT_SIZES; ++m)
648         {
649                 if (!maps[m])
650                         continue;
651                 // "round up" to the bigger size if two equally-valued matches exist
652                 nval = 0.5 * (abs(maps[m]->size - fsize_x) + abs(maps[m]->size - fsize_y));
653                 if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
654                 {
655                         value = nval;
656                         match = m;
657                         matchsize = maps[m]->size;
658                         if (value == 0) // there is no better match
659                                 break;
660                 }
661         }
662         if (value <= r_font_size_snapping.value)
663         {
664                 // do NOT keep the aspect for perfect rendering
665                 if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
666                 if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width;
667         }
668         return match;
669 }
670
671 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
672 {
673         if (index < 0 || index >= MAX_FONT_SIZES)
674                 return NULL;
675         return font->font_maps[index];
676 }
677
678 static qboolean Font_SetSize(ft2_font_t *font, float w, float h)
679 {
680         if (font->currenth == h &&
681             ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
682              font->currentw == w)) // same size has been requested
683         {
684                 return true;
685         }
686         // sorry, but freetype doesn't seem to care about other sizes
687         w = (int)w;
688         h = (int)h;
689         if (font->image_font)
690         {
691                 if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
692                         return false;
693         }
694         else
695         {
696                 if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
697                         return false;
698         }
699         font->currentw = w;
700         font->currenth = h;
701         return true;
702 }
703
704 qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
705 {
706         ft2_font_map_t *fmap;
707         if (!font->has_kerning || !r_font_kerning.integer)
708                 return false;
709         if (map_index < 0 || map_index >= MAX_FONT_SIZES)
710                 return false;
711         fmap = font->font_maps[map_index];
712         if (!fmap)
713                 return false;
714         if (left < 256 && right < 256)
715         {
716                 //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
717                 // quick-kerning, be aware of the size: scale it
718                 if (outx) *outx = fmap->kerning.kerning[left][right][0];// * (w / (float)fmap->size);
719                 if (outy) *outy = fmap->kerning.kerning[left][right][1];// * (h / (float)fmap->size);
720                 return true;
721         }
722         else
723         {
724                 FT_Vector kernvec;
725                 FT_ULong ul, ur;
726
727                 //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
728 #if 0
729                 if (!Font_SetSize(font, w, h))
730                 {
731                         // this deserves an error message
732                         Con_Printf("Failed to get kerning for %s\n", font->name);
733                         return false;
734                 }
735                 ul = qFT_Get_Char_Index(font->face, left);
736                 ur = qFT_Get_Char_Index(font->face, right);
737                 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
738                 {
739                         if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);
740                         if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);
741                         return true;
742                 }
743 #endif
744                 if (!Font_SetSize(font, fmap->intSize, fmap->intSize))
745                 {
746                         // this deserves an error message
747                         Con_Printf("Failed to get kerning for %s\n", font->name);
748                         return false;
749                 }
750                 ul = qFT_Get_Char_Index(font->face, left);
751                 ur = qFT_Get_Char_Index(font->face, right);
752                 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
753                 {
754                         if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);// * (w / (float)fmap->size);
755                         if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);// * (h / (float)fmap->size);
756                         return true;
757                 }
758                 return false;
759         }
760 }
761
762 qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
763 {
764         return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
765 }
766
767 static void UnloadMapRec(ft2_font_map_t *map)
768 {
769         if (map->texture)
770         {
771                 R_FreeTexture(map->texture);
772                 map->texture = NULL;
773         }
774         if (map->next)
775                 UnloadMapRec(map->next);
776         Mem_Free(map);
777 }
778
779 void Font_UnloadFont(ft2_font_t *font)
780 {
781         int i;
782         if (font->attachments && font->attachmentcount)
783         {
784                 Mem_Free(font->attachments);
785                 font->attachmentcount = 0;
786                 font->attachments = NULL;
787         }
788         for (i = 0; i < MAX_FONT_SIZES; ++i)
789         {
790                 if (font->font_maps[i])
791                 {
792                         UnloadMapRec(font->font_maps[i]);
793                         font->font_maps[i] = NULL;
794                 }
795         }
796         if (ft2_dll)
797         {
798                 if (font->face)
799                 {
800                         qFT_Done_Face((FT_Face)font->face);
801                         font->face = NULL;
802                 }
803         }
804 }
805
806 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size)
807 {
808         float intSize = size;
809         while (1)
810         {
811                 if (!Font_SetSize(font, intSize, intSize))
812                 {
813                         Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, size, intSize);
814                         return -1;
815                 }
816                 if ((fontface->size->metrics.height>>6) <= size)
817                         return intSize;
818                 if (intSize < 2)
819                 {
820                         Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, size);
821                         return -1;
822                 }
823                 --intSize;
824         }
825 }
826
827 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
828 {
829         char map_identifier[MAX_QPATH];
830         unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
831         unsigned char *data;
832         FT_ULong ch, mapch;
833         int status;
834         int tp;
835         FT_Int32 load_flags;
836         int gpad_l, gpad_r, gpad_t, gpad_b;
837
838         int pitch;
839         int gR, gC; // glyph position: row and column
840
841         ft2_font_map_t *map, *next;
842         ft2_font_t *usefont;
843
844         FT_Face fontface;
845
846         int bytesPerPixel = 4; // change the conversion loop too if you change this!
847
848         if (outmap)
849                 *outmap = NULL;
850
851         if (r_font_use_alpha_textures.integer)
852                 bytesPerPixel = 1;
853
854         if (font->image_font)
855                 fontface = (FT_Face)font->next->face;
856         else
857                 fontface = (FT_Face)font->face;
858
859         switch(r_font_antialias.integer)
860         {
861                 case 0:
862                         switch(r_font_hinting.integer)
863                         {
864                                 case 0:
865                                         load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
866                                         break;
867                                 case 1:
868                                 case 2:
869                                         load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
870                                         break;
871                                 default:
872                                 case 3:
873                                         load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
874                                         break;
875                         }
876                         break;
877                 default:
878                 case 1:
879                         switch(r_font_hinting.integer)
880                         {
881                                 case 0:
882                                         load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL;
883                                         break;
884                                 case 1:
885                                         load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
886                                         break;
887                                 case 2:
888                                         load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
889                                         break;
890                                 default:
891                                 case 3:
892                                         load_flags = FT_LOAD_TARGET_NORMAL;
893                                         break;
894                         }
895                         break;
896         }
897
898         //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
899         //if (status)
900         if (font->image_font && mapstart->intSize < 0)
901                 mapstart->intSize = mapstart->size;
902         if (mapstart->intSize < 0)
903         {
904                 /*
905                 mapstart->intSize = mapstart->size;
906                 while (1)
907                 {
908                         if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
909                         {
910                                 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
911                                 return false;
912                         }
913                         if ((fontface->size->metrics.height>>6) <= mapstart->size)
914                                 break;
915                         if (mapstart->intSize < 2)
916                         {
917                                 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
918                                 return false;
919                         }
920                         --mapstart->intSize;
921                 }
922                 */
923                 if ((mapstart->intSize = Font_SearchSize(font, fontface, mapstart->size)) <= 0)
924                         return false;
925                 Con_DPrintf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
926         }
927
928         if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
929         {
930                 Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
931                 return false;
932         }
933
934         map = Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
935         if (!map)
936         {
937                 Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
938                 return false;
939         }
940
941         Font_Postprocess(NULL, 0, 0, 0, 0, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
942
943         // copy over the information
944         map->size = mapstart->size;
945         map->intSize = mapstart->intSize;
946         map->glyphSize = mapstart->glyphSize;
947         map->sfx = mapstart->sfx;
948         map->sfy = mapstart->sfy;
949
950         pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
951         data = Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
952         if (!data)
953         {
954                 Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
955                 Mem_Free(map);
956                 return false;
957         }
958         memset(map->width_of, 0, sizeof(map->width_of));
959
960         // initialize as white texture with zero alpha
961         tp = 0;
962         while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
963         {
964                 if (bytesPerPixel == 4)
965                 {
966                         data[tp++] = 0xFF;
967                         data[tp++] = 0xFF;
968                         data[tp++] = 0xFF;
969                 }
970                 data[tp++] = 0x00;
971         }
972
973         // insert the map
974         map->start = mapidx * FONT_CHARS_PER_MAP;
975         next = mapstart;
976         while(next->next && next->next->start < map->start)
977                 next = next->next;
978         map->next = next->next;
979         next->next = map;
980
981         gR = 0;
982         gC = -1;
983         for (ch = map->start;
984              ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
985              ++ch)
986         {
987                 FT_ULong glyphIndex;
988                 int w, h, x, y;
989                 FT_GlyphSlot glyph;
990                 FT_Bitmap *bmp;
991                 unsigned char *imagedata, *dst, *src;
992                 glyph_slot_t *mapglyph;
993                 FT_Face face;
994                 int pad_l, pad_r, pad_t, pad_b;
995
996                 mapch = ch - map->start;
997
998                 if (developer_font.integer)
999                         Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
1000
1001                 ++gC;
1002                 if (gC >= FONT_CHARS_PER_LINE)
1003                 {
1004                         gC -= FONT_CHARS_PER_LINE;
1005                         ++gR;
1006                 }
1007
1008                 imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
1009                 imagedata += gpad_t * pitch + gpad_l;
1010                 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
1011                 // we need the glyphIndex
1012                 face = font->face;
1013                 usefont = NULL;
1014                 if (font->image_font && mapch == ch && img_fontmap[mapch])
1015                 {
1016                         map->glyphs[mapch].image = true;
1017                         continue;
1018                 }
1019                 glyphIndex = qFT_Get_Char_Index(face, ch);
1020                 if (glyphIndex == 0)
1021                 {
1022                         // by convention, 0 is the "missing-glyph"-glyph
1023                         // try to load from a fallback font
1024                         for(usefont = font->next; usefont != NULL; usefont = usefont->next)
1025                         {
1026                                 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
1027                                         continue;
1028                                 // try that glyph
1029                                 face = usefont->face;
1030                                 glyphIndex = qFT_Get_Char_Index(face, ch);
1031                                 if (glyphIndex == 0)
1032                                         continue;
1033                                 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1034                                 if (status)
1035                                         continue;
1036                                 break;
1037                         }
1038                         if (!usefont)
1039                         {
1040                                 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1041                                 // now we let it use the "missing-glyph"-glyph
1042                                 face = font->face;
1043                                 glyphIndex = 0;
1044                         }
1045                 }
1046
1047                 if (!usefont)
1048                 {
1049                         usefont = font;
1050                         face = font->face;
1051                         status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1052                         if (status)
1053                         {
1054                                 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1055                                 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1056                                 continue;
1057                         }
1058                 }
1059
1060                 glyph = face->glyph;
1061                 bmp = &glyph->bitmap;
1062
1063                 w = bmp->width;
1064                 h = bmp->rows;
1065
1066                 if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
1067                         Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
1068                         if (w > map->glyphSize)
1069                                 w = map->glyphSize - gpad_l - gpad_r;
1070                         if (h > map->glyphSize)
1071                                 h = map->glyphSize;
1072                 }
1073
1074                 switch (bmp->pixel_mode)
1075                 {
1076                 case FT_PIXEL_MODE_MONO:
1077                         if (developer_font.integer)
1078                                 Con_DPrint("glyphinfo:   Pixel Mode: MONO\n");
1079                         break;
1080                 case FT_PIXEL_MODE_GRAY2:
1081                         if (developer_font.integer)
1082                                 Con_DPrint("glyphinfo:   Pixel Mode: GRAY2\n");
1083                         break;
1084                 case FT_PIXEL_MODE_GRAY4:
1085                         if (developer_font.integer)
1086                                 Con_DPrint("glyphinfo:   Pixel Mode: GRAY4\n");
1087                         break;
1088                 case FT_PIXEL_MODE_GRAY:
1089                         if (developer_font.integer)
1090                                 Con_DPrint("glyphinfo:   Pixel Mode: GRAY\n");
1091                         break;
1092                 default:
1093                         if (developer_font.integer)
1094                                 Con_DPrintf("glyphinfo:   Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1095                         Mem_Free(data);
1096                         Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1097                         return false;
1098                 }
1099                 for (y = 0; y < h; ++y)
1100                 {
1101                         dst = imagedata + y * pitch;
1102                         src = bmp->buffer + y * bmp->pitch;
1103
1104                         switch (bmp->pixel_mode)
1105                         {
1106                         case FT_PIXEL_MODE_MONO:
1107                                 dst += bytesPerPixel - 1; // shift to alpha byte
1108                                 for (x = 0; x < bmp->width; x += 8)
1109                                 {
1110                                         unsigned char ch = *src++;
1111                                         *dst = 255 * !!((ch & 0x80) >> 7); dst += bytesPerPixel;
1112                                         *dst = 255 * !!((ch & 0x40) >> 6); dst += bytesPerPixel;
1113                                         *dst = 255 * !!((ch & 0x20) >> 5); dst += bytesPerPixel;
1114                                         *dst = 255 * !!((ch & 0x10) >> 4); dst += bytesPerPixel;
1115                                         *dst = 255 * !!((ch & 0x08) >> 3); dst += bytesPerPixel;
1116                                         *dst = 255 * !!((ch & 0x04) >> 2); dst += bytesPerPixel;
1117                                         *dst = 255 * !!((ch & 0x02) >> 1); dst += bytesPerPixel;
1118                                         *dst = 255 * !!((ch & 0x01) >> 0); dst += bytesPerPixel;
1119                                 }
1120                                 break;
1121                         case FT_PIXEL_MODE_GRAY2:
1122                                 dst += bytesPerPixel - 1; // shift to alpha byte
1123                                 for (x = 0; x < bmp->width; x += 4)
1124                                 {
1125                                         unsigned char ch = *src++;
1126                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1127                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1128                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1129                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1130                                 }
1131                                 break;
1132                         case FT_PIXEL_MODE_GRAY4:
1133                                 dst += bytesPerPixel - 1; // shift to alpha byte
1134                                 for (x = 0; x < bmp->width; x += 2)
1135                                 {
1136                                         unsigned char ch = *src++;
1137                                         *dst = ( ((ch & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
1138                                         *dst = ( ((ch & 0x0F) ) * 0x11); dst += bytesPerPixel;
1139                                 }
1140                                 break;
1141                         case FT_PIXEL_MODE_GRAY:
1142                                 // in this case pitch should equal width
1143                                 for (tp = 0; tp < bmp->pitch; ++tp)
1144                                         dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
1145
1146                                 //memcpy((void*)dst, (void*)src, bmp->pitch);
1147                                 //dst += bmp->pitch;
1148                                 break;
1149                         default:
1150                                 break;
1151                         }
1152                 }
1153
1154                 pad_l = gpad_l;
1155                 pad_r = gpad_r;
1156                 pad_t = gpad_t;
1157                 pad_b = gpad_b;
1158                 Font_Postprocess(imagedata, pitch, w, h, bytesPerPixel, &pad_l, &pad_r, &pad_t, &pad_b);
1159
1160                 // now fill map->glyphs[ch - map->start]
1161                 mapglyph = &map->glyphs[mapch];
1162
1163                 {
1164                         // old way
1165                         // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1166
1167                         double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1168                         //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1169                         double advance = (glyph->advance.x / 64.0) / map->size;
1170                         //double mWidth = (glyph->metrics.width >> 6) / map->size;
1171                         //double mHeight = (glyph->metrics.height >> 6) / map->size;
1172
1173                         mapglyph->txmin = ( (double)(gC * map->glyphSize) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1174                         mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1175                         mapglyph->tymin = ( (double)(gR * map->glyphSize) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1176                         mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1177                         //mapglyph->vxmin = bearingX;
1178                         //mapglyph->vxmax = bearingX + mWidth;
1179                         mapglyph->vxmin = glyph->bitmap_left / map->size;
1180                         mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
1181                         //mapglyph->vymin = -bearingY;
1182                         //mapglyph->vymax = mHeight - bearingY;
1183                         mapglyph->vymin = -glyph->bitmap_top / map->size;
1184                         mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
1185                         //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);
1186                         //mapglyph->advance_x = advance * usefont->size;
1187                         //mapglyph->advance_x = advance;
1188                         mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1189                         mapglyph->advance_y = 0;
1190
1191                         if (developer_font.integer)
1192                         {
1193                                 Con_DPrintf("glyphinfo:   Glyph: %lu   at (%i, %i)\n", (unsigned long)ch, gC, gR);
1194                                 Con_DPrintf("glyphinfo:   %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1195                                 if (ch >= 32 && ch <= 128)
1196                                         Con_DPrintf("glyphinfo:   Character: %c\n", (int)ch);
1197                                 Con_DPrintf("glyphinfo:   Vertex info:\n");
1198                                 Con_DPrintf("glyphinfo:     X: ( %f  --  %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1199                                 Con_DPrintf("glyphinfo:     Y: ( %f  --  %f )\n", mapglyph->vymin, mapglyph->vymax);
1200                                 Con_DPrintf("glyphinfo:   Texture info:\n");
1201                                 Con_DPrintf("glyphinfo:     S: ( %f  --  %f )\n", mapglyph->txmin, mapglyph->txmax);
1202                                 Con_DPrintf("glyphinfo:     T: ( %f  --  %f )\n", mapglyph->tymin, mapglyph->tymax);
1203                                 Con_DPrintf("glyphinfo:   Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1204                         }
1205                 }
1206                 map->glyphs[mapch].image = false;
1207         }
1208
1209         // create a texture from the data now
1210
1211         if (developer_font.integer > 100)
1212         {
1213                 // LordHavoc: why are we writing this?  And why not write it as TGA using the appropriate function?
1214                 // view using `display -depth 8 -size 512x512 name_page.rgba` (be sure to use a correct -size parameter)
1215                 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u.rgba", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1216                 FS_WriteFile(map_identifier, data, pitch * FONT_CHAR_LINES * map->glyphSize);
1217         }
1218         dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1219
1220         // probably use bytesPerPixel here instead?
1221         if (r_font_use_alpha_textures.integer)
1222         {
1223                 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1224                                                map->glyphSize * FONT_CHARS_PER_LINE,
1225                                                map->glyphSize * FONT_CHAR_LINES,
1226                                                data, TEXTYPE_ALPHA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL);
1227         } else {
1228                 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1229                                                map->glyphSize * FONT_CHARS_PER_LINE,
1230                                                map->glyphSize * FONT_CHAR_LINES,
1231                                                data, TEXTYPE_RGBA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL);
1232         }
1233
1234         Mem_Free(data);
1235         if (!map->texture)
1236         {
1237                 // if the first try isn't successful, keep it with a broken texture
1238                 // otherwise we retry to load it every single frame where ft2 rendering is used
1239                 // this would be bad...
1240                 // only `data' must be freed
1241                 Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
1242                            font->name, mapstart->size, mapidx);
1243                 return false;
1244         }
1245         if (outmap)
1246                 *outmap = map;
1247         return true;
1248 }
1249
1250 qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
1251 {
1252         if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1253                 return false;
1254         // the first map must have been loaded already
1255         if (!font->font_maps[map_index])
1256                 return false;
1257         return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
1258 }
1259
1260 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1261 {
1262         while (start && start->start + FONT_CHARS_PER_MAP <= ch)
1263                 start = start->next;
1264         if (start && start->start > ch)
1265                 return NULL;
1266         return start;
1267 }