]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - ft2.c
cache multiple loaded instances of the same font (TODO: can we also cache the freetyp...
[xonotic/darkplaces.git] / ft2.c
1 /* FreeType 2 and UTF-8 encoding support for
2  * DarkPlaces
3  */
4 #include "quakedef.h"
5
6 #include "ft2.h"
7 #include "ft2_defs.h"
8 #include "ft2_fontdefs.h"
9
10 static int img_fontmap[256] = {
11         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
12         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
13         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // shift+digit line
14         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // digits
15         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // caps
16         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // caps
17         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small
18         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small
19         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // specials
20         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // faces
21         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
22         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
23         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
24         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
25         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
26         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
27 };
28
29 /*
30 ================================================================================
31 CVars introduced with the freetype extension
32 ================================================================================
33 */
34
35 cvar_t r_font_disable_freetype = {CVAR_SAVE, "r_font_disable_freetype", "1", "disable freetype support for fonts entirely"};
36 cvar_t r_font_use_alpha_textures = {CVAR_SAVE, "r_font_use_alpha_textures", "0", "use alpha-textures for font rendering, this should safe memory"};
37 cvar_t r_font_size_snapping = {CVAR_SAVE, "r_font_size_snapping", "1", "stick to good looking font sizes whenever possible - bad when the mod doesn't support it!"};
38 cvar_t r_font_kerning = {CVAR_SAVE, "r_font_kerning", "1", "Use kerning if available"};
39 cvar_t developer_font = {CVAR_SAVE, "developer_font", "0", "prints debug messages about fonts"};
40
41 /*
42 ================================================================================
43 Function definitions. Taken from the freetype2 headers.
44 ================================================================================
45 */
46
47
48 FT_EXPORT( FT_Error )
49 (*qFT_Init_FreeType)( FT_Library  *alibrary );
50 FT_EXPORT( FT_Error )
51 (*qFT_Done_FreeType)( FT_Library  library );
52 /*
53 FT_EXPORT( FT_Error )
54 (*qFT_New_Face)( FT_Library   library,
55                  const char*  filepathname,
56                  FT_Long      face_index,
57                  FT_Face     *aface );
58 */
59 FT_EXPORT( FT_Error )
60 (*qFT_New_Memory_Face)( FT_Library      library,
61                         const FT_Byte*  file_base,
62                         FT_Long         file_size,
63                         FT_Long         face_index,
64                         FT_Face        *aface );
65 FT_EXPORT( FT_Error )
66 (*qFT_Done_Face)( FT_Face  face );
67 FT_EXPORT( FT_Error )
68 (*qFT_Select_Size)( FT_Face  face,
69                     FT_Int   strike_index );
70 FT_EXPORT( FT_Error )
71 (*qFT_Request_Size)( FT_Face          face,
72                      FT_Size_Request  req );
73 FT_EXPORT( FT_Error )
74 (*qFT_Set_Char_Size)( FT_Face     face,
75                       FT_F26Dot6  char_width,
76                       FT_F26Dot6  char_height,
77                       FT_UInt     horz_resolution,
78                       FT_UInt     vert_resolution );
79 FT_EXPORT( FT_Error )
80 (*qFT_Set_Pixel_Sizes)( FT_Face  face,
81                         FT_UInt  pixel_width,
82                         FT_UInt  pixel_height );
83 FT_EXPORT( FT_Error )
84 (*qFT_Load_Glyph)( FT_Face   face,
85                    FT_UInt   glyph_index,
86                    FT_Int32  load_flags );
87 FT_EXPORT( FT_Error )
88 (*qFT_Load_Char)( FT_Face   face,
89                   FT_ULong  char_code,
90                   FT_Int32  load_flags );
91 FT_EXPORT( FT_UInt )
92 (*qFT_Get_Char_Index)( FT_Face   face,
93                        FT_ULong  charcode );
94 FT_EXPORT( FT_Error )
95 (*qFT_Render_Glyph)( FT_GlyphSlot    slot,
96                      FT_Render_Mode  render_mode );
97 FT_EXPORT( FT_Error )
98 (*qFT_Get_Kerning)( FT_Face     face,
99                     FT_UInt     left_glyph,
100                     FT_UInt     right_glyph,
101                     FT_UInt     kern_mode,
102                     FT_Vector  *akerning );
103 FT_EXPORT( FT_Error )
104 (*qFT_Attach_Stream)( FT_Face        face,
105                       FT_Open_Args*  parameters );
106 /*
107 ================================================================================
108 Support for dynamically loading the FreeType2 library
109 ================================================================================
110 */
111
112 static dllfunction_t ft2funcs[] =
113 {
114         {"FT_Init_FreeType",            (void **) &qFT_Init_FreeType},
115         {"FT_Done_FreeType",            (void **) &qFT_Done_FreeType},
116         //{"FT_New_Face",                       (void **) &qFT_New_Face},
117         {"FT_New_Memory_Face",          (void **) &qFT_New_Memory_Face},
118         {"FT_Done_Face",                (void **) &qFT_Done_Face},
119         {"FT_Select_Size",              (void **) &qFT_Select_Size},
120         {"FT_Request_Size",             (void **) &qFT_Request_Size},
121         {"FT_Set_Char_Size",            (void **) &qFT_Set_Char_Size},
122         {"FT_Set_Pixel_Sizes",          (void **) &qFT_Set_Pixel_Sizes},
123         {"FT_Load_Glyph",               (void **) &qFT_Load_Glyph},
124         {"FT_Load_Char",                (void **) &qFT_Load_Char},
125         {"FT_Get_Char_Index",           (void **) &qFT_Get_Char_Index},
126         {"FT_Render_Glyph",             (void **) &qFT_Render_Glyph},
127         {"FT_Get_Kerning",              (void **) &qFT_Get_Kerning},
128         {"FT_Attach_Stream",            (void **) &qFT_Attach_Stream},
129         {NULL, NULL}
130 };
131
132 /// Handle for FreeType2 DLL
133 static dllhandle_t ft2_dll = NULL;
134
135 /// Memory pool for fonts
136 static mempool_t *font_mempool= NULL;
137 static rtexturepool_t *font_texturepool = NULL;
138
139 /// FreeType library handle
140 static FT_Library font_ft2lib = NULL;
141
142 #define POSTPROCESS_MAXRADIUS 8
143 typedef struct
144 {
145         unsigned char *buf, *buf2;
146         int bufsize, bufwidth, bufheight, bufpitch;
147         float blur, outline, shadowx, shadowy, shadowz;
148         int padding_t, padding_b, padding_l, padding_r, blurpadding_lt, blurpadding_rb, outlinepadding_t, outlinepadding_b, outlinepadding_l, outlinepadding_r;
149         unsigned char circlematrix[2*POSTPROCESS_MAXRADIUS+1][2*POSTPROCESS_MAXRADIUS+1];
150         unsigned char gausstable[2*POSTPROCESS_MAXRADIUS+1];
151 }
152 font_postprocess_t;
153 static font_postprocess_t pp;
154
155 typedef struct fontfilecache_s
156 {
157         unsigned char *buf;
158         fs_offset_t len;
159         int refcount;
160         char path[MAX_QPATH];
161 }
162 fontfilecache_t;
163 #define MAX_FONTFILES 8
164 static fontfilecache_t fontfiles[MAX_FONTFILES];
165 static const unsigned char *fontfilecache_LoadFile(const char *path, qboolean quiet, fs_offset_t *filesizepointer)
166 {
167         int i;
168         unsigned char *buf;
169
170         for(i = 0; i < MAX_FONTFILES; ++i)
171         {
172                 if(fontfiles[i].refcount > 0)
173                         if(!strcmp(path, fontfiles[i].path))
174                         {
175                                 *filesizepointer = fontfiles[i].len;
176                                 ++fontfiles[i].refcount;
177                                 return fontfiles[i].buf;
178                         }
179         }
180
181         buf = FS_LoadFile(path, font_mempool, quiet, filesizepointer);
182         if(buf)
183         {
184                 for(i = 0; i < MAX_FONTFILES; ++i)
185                         if(fontfiles[i].refcount <= 0)
186                         {
187                                 strlcpy(fontfiles[i].path, path, sizeof(fontfiles[i].path));
188                                 fontfiles[i].len = *filesizepointer;
189                                 fontfiles[i].buf = buf;
190                                 fontfiles[i].refcount = 1;
191                                 return buf;
192                         }
193         }
194
195         return buf;
196 }
197 static void fontfilecache_Free(const unsigned char *buf)
198 {
199         int i;
200         for(i = 0; i < MAX_FONTFILES; ++i)
201         {
202                 if(fontfiles[i].refcount > 0)
203                         if(fontfiles[i].buf == buf)
204                         {
205                                 if(--fontfiles[i].refcount <= 0)
206                                 {
207                                         Mem_Free(fontfiles[i].buf);
208                                         fontfiles[i].buf = NULL;
209                                 }
210                                 return;
211                         }
212         }
213         // if we get here, it used regular allocation
214         Mem_Free((void *) buf);
215 }
216 static void fontfilecache_FreeAll(void)
217 {
218         int i;
219         for(i = 0; i < MAX_FONTFILES; ++i)
220         {
221                 if(fontfiles[i].refcount > 0)
222                         Mem_Free(fontfiles[i].buf);
223                 fontfiles[i].buf = NULL;
224                 fontfiles[i].refcount = 0;
225         }
226 }
227
228 /*
229 ====================
230 Font_CloseLibrary
231
232 Unload the FreeType2 DLL
233 ====================
234 */
235 void Font_CloseLibrary (void)
236 {
237         fontfilecache_FreeAll();
238         if (font_mempool)
239                 Mem_FreePool(&font_mempool);
240         if (font_texturepool)
241                 R_FreeTexturePool(&font_texturepool);
242         if (font_ft2lib && qFT_Done_FreeType)
243         {
244                 qFT_Done_FreeType(font_ft2lib);
245                 font_ft2lib = NULL;
246         }
247         Sys_UnloadLibrary (&ft2_dll);
248         pp.buf = NULL;
249 }
250
251 /*
252 ====================
253 Font_OpenLibrary
254
255 Try to load the FreeType2 DLL
256 ====================
257 */
258 qboolean Font_OpenLibrary (void)
259 {
260         const char* dllnames [] =
261         {
262 #if defined(WIN32)
263                 "libfreetype-6.dll",
264                 "freetype6.dll",
265 #elif defined(MACOSX)
266                 "libfreetype.6.dylib",
267                 "libfreetype.dylib",
268 #else
269                 "libfreetype.so.6",
270                 "libfreetype.so",
271 #endif
272                 NULL
273         };
274
275         if (r_font_disable_freetype.integer)
276                 return false;
277
278         // Already loaded?
279         if (ft2_dll)
280                 return true;
281
282         // Load the DLL
283         if (!Sys_LoadLibrary (dllnames, &ft2_dll, ft2funcs))
284                 return false;
285         return true;
286 }
287
288 /*
289 ====================
290 Font_Init
291
292 Initialize the freetype2 font subsystem
293 ====================
294 */
295
296 void font_start(void)
297 {
298         if (!Font_OpenLibrary())
299                 return;
300
301         if (qFT_Init_FreeType(&font_ft2lib))
302         {
303                 Con_Print("ERROR: Failed to initialize the FreeType2 library!\n");
304                 Font_CloseLibrary();
305                 return;
306         }
307
308         font_mempool = Mem_AllocPool("FONT", 0, NULL);
309         if (!font_mempool)
310         {
311                 Con_Print("ERROR: Failed to allocate FONT memory pool!\n");
312                 Font_CloseLibrary();
313                 return;
314         }
315
316         font_texturepool = R_AllocTexturePool();
317         if (!font_texturepool)
318         {
319                 Con_Print("ERROR: Failed to allocate FONT texture pool!\n");
320                 Font_CloseLibrary();
321                 return;
322         }
323 }
324
325 void font_shutdown(void)
326 {
327         int i;
328         for (i = 0; i < dp_fonts.maxsize; ++i)
329         {
330                 if (dp_fonts.f[i].ft2)
331                 {
332                         Font_UnloadFont(dp_fonts.f[i].ft2);
333                         dp_fonts.f[i].ft2 = NULL;
334                 }
335         }
336         Font_CloseLibrary();
337 }
338
339 void font_newmap(void)
340 {
341 }
342
343 void Font_Init(void)
344 {
345         Cvar_RegisterVariable(&r_font_disable_freetype);
346         Cvar_RegisterVariable(&r_font_use_alpha_textures);
347         Cvar_RegisterVariable(&r_font_size_snapping);
348         Cvar_RegisterVariable(&r_font_kerning);
349         Cvar_RegisterVariable(&developer_font);
350
351         // let's open it at startup already
352         Font_OpenLibrary();
353 }
354
355 /*
356 ================================================================================
357 Implementation of a more or less lazy font loading and rendering code.
358 ================================================================================
359 */
360
361 #include "ft2_fontdefs.h"
362
363 ft2_font_t *Font_Alloc(void)
364 {
365         if (!ft2_dll)
366                 return NULL;
367         return (ft2_font_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_t));
368 }
369
370 qboolean Font_Attach(ft2_font_t *font, ft2_attachment_t *attachment)
371 {
372         ft2_attachment_t *na;
373
374         font->attachmentcount++;
375         na = (ft2_attachment_t*)Mem_Alloc(font_mempool, sizeof(font->attachments[0]) * font->attachmentcount);
376         if (na == NULL)
377                 return false;
378         if (font->attachments && font->attachmentcount > 1)
379         {
380                 memcpy(na, font->attachments, sizeof(font->attachments[0]) * (font->attachmentcount - 1));
381                 Mem_Free(font->attachments);
382         }
383         memcpy(na + sizeof(font->attachments[0]) * (font->attachmentcount - 1), attachment, sizeof(*attachment));
384         font->attachments = na;
385         return true;
386 }
387
388 float Font_VirtualToRealSize(float sz)
389 {
390         int vh;
391         //int vw;
392         int si;
393         float sn;
394         if(sz < 0)
395                 return sz;
396         //vw = ((vid.width > 0) ? vid.width : vid_width.value);
397         vh = ((vid.height > 0) ? vid.height : vid_height.value);
398         // now try to scale to our actual size:
399         sn = sz * vh / vid_conheight.value;
400         si = (int)sn;
401         if ( sn - (float)si >= 0.5 )
402                 ++si;
403         return si;
404 }
405
406 float Font_SnapTo(float val, float snapwidth)
407 {
408         return floor(val / snapwidth + 0.5f) * snapwidth;
409 }
410
411 static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font);
412 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only);
413 qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt)
414 {
415         int s, count, i;
416         ft2_font_t *ft2, *fbfont, *fb;
417
418         ft2 = Font_Alloc();
419         if (!ft2)
420         {
421                 dpfnt->ft2 = NULL;
422                 return false;
423         }
424
425         // check if a fallback font has been specified, if it has been, and the
426         // font fails to load, use the image font as main font
427         for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
428         {
429                 if (dpfnt->fallbacks[i][0])
430                         break;
431         }
432
433         if (!Font_LoadFile(name, dpfnt->req_face, &dpfnt->settings, ft2))
434         {
435                 if (i >= MAX_FONT_FALLBACKS)
436                 {
437                         dpfnt->ft2 = NULL;
438                         Mem_Free(ft2);
439                         return false;
440                 }
441                 strlcpy(ft2->name, name, sizeof(ft2->name));
442                 ft2->image_font = true;
443                 ft2->has_kerning = false;
444         }
445         else
446         {
447                 ft2->image_font = false;
448         }
449
450         // attempt to load fallback fonts:
451         fbfont = ft2;
452         for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
453         {
454                 if (!dpfnt->fallbacks[i][0])
455                         break;
456                 if (! (fb = Font_Alloc()) )
457                 {
458                         Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
459                         break;
460                 }
461
462                 if (!Font_LoadFile(dpfnt->fallbacks[i], dpfnt->fallback_faces[i], &dpfnt->settings, fb))
463                 {
464                         if(!FS_FileExists(va("%s.tga", dpfnt->fallbacks[i])))
465                         if(!FS_FileExists(va("%s.png", dpfnt->fallbacks[i])))
466                         if(!FS_FileExists(va("%s.jpg", dpfnt->fallbacks[i])))
467                         if(!FS_FileExists(va("%s.pcx", dpfnt->fallbacks[i])))
468                                 Con_Printf("Failed to load font %s for fallback %i of font %s\n", dpfnt->fallbacks[i], i, name);
469                         Mem_Free(fb);
470                         continue;
471                 }
472                 count = 0;
473                 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
474                 {
475                         if (Font_LoadSize(fb, Font_VirtualToRealSize(dpfnt->req_sizes[s]), true))
476                                 ++count;
477                 }
478                 if (!count)
479                 {
480                         Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
481                         Font_UnloadFont(fb);
482                         Mem_Free(fb);
483                         break;
484                 }
485                 // at least one size of the fallback font loaded successfully
486                 // link it:
487                 fbfont->next = fb;
488                 fbfont = fb;
489         }
490
491         if (fbfont == ft2 && ft2->image_font)
492         {
493                 // no fallbacks were loaded successfully:
494                 dpfnt->ft2 = NULL;
495                 Mem_Free(ft2);
496                 return false;
497         }
498
499         count = 0;
500         for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
501         {
502                 if (Font_LoadSize(ft2, Font_VirtualToRealSize(dpfnt->req_sizes[s]), false))
503                         ++count;
504         }
505         if (!count)
506         {
507                 // loading failed for every requested size
508                 Font_UnloadFont(ft2);
509                 Mem_Free(ft2);
510                 dpfnt->ft2 = NULL;
511                 return false;
512         }
513
514         //Con_Printf("%i sizes loaded\n", count);
515         dpfnt->ft2 = ft2;
516         return true;
517 }
518
519 static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font)
520 {
521         size_t namelen;
522         char filename[MAX_QPATH];
523         int status;
524         size_t i;
525         const unsigned char *data;
526         fs_offset_t datasize;
527
528         memset(font, 0, sizeof(*font));
529
530         if (!Font_OpenLibrary())
531         {
532                 if (!r_font_disable_freetype.integer)
533                 {
534                         Con_Printf("WARNING: can't open load font %s\n"
535                                    "You need the FreeType2 DLL to load font files\n",
536                                    name);
537                 }
538                 return false;
539         }
540
541         font->settings = settings;
542
543         namelen = strlen(name);
544
545         // try load direct file
546         memcpy(filename, name, namelen+1);
547         data = fontfilecache_LoadFile(filename, false, &datasize);
548         // try load .ttf
549         if (!data)
550         {
551                 memcpy(filename + namelen, ".ttf", 5);
552                 data = fontfilecache_LoadFile(filename, false, &datasize);
553         }
554         // try load .otf
555         if (!data)
556         {
557                 memcpy(filename + namelen, ".otf", 5);
558                 data = fontfilecache_LoadFile(filename, false, &datasize);
559         }
560         // try load .pfb/afm
561         if (!data)
562         {
563                 ft2_attachment_t afm;
564
565                 memcpy(filename + namelen, ".pfb", 5);
566                 data = fontfilecache_LoadFile(filename, false, &datasize);
567
568                 if (data)
569                 {
570                         memcpy(filename + namelen, ".afm", 5);
571                         afm.data = fontfilecache_LoadFile(filename, false, &afm.size);
572
573                         if (afm.data)
574                                 Font_Attach(font, &afm);
575                 }
576         }
577         if (!data)
578         {
579                 // FS_LoadFile being not-quiet should print an error :)
580                 return false;
581         }
582         Con_DPrintf("Loading font %s face %i...\n", filename, _face);
583
584         status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
585         if (status && _face != 0)
586         {
587                 Con_Printf("Failed to load face %i of %s. Falling back to face 0\n", _face, name);
588                 _face = 0;
589                 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, 0, (FT_Face*)&font->face);
590         }
591         font->data = data;
592         if (status)
593         {
594                 Con_Printf("ERROR: can't create face for %s\n"
595                            "Error %i\n", // TODO: error strings
596                            name, status);
597                 Font_UnloadFont(font);
598                 return false;
599         }
600
601         // add the attachments
602         for (i = 0; i < font->attachmentcount; ++i)
603         {
604                 FT_Open_Args args;
605                 memset(&args, 0, sizeof(args));
606                 args.flags = FT_OPEN_MEMORY;
607                 args.memory_base = (const FT_Byte*)font->attachments[i].data;
608                 args.memory_size = font->attachments[i].size;
609                 if (qFT_Attach_Stream((FT_Face)font->face, &args))
610                         Con_Printf("Failed to add attachment %u to %s\n", (unsigned)i, font->name);
611         }
612
613         memcpy(font->name, name, namelen+1);
614         font->image_font = false;
615         font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
616         return true;
617 }
618
619 void Font_Postprocess_Update(ft2_font_t *fnt, int bpp, int w, int h)
620 {
621         int needed, x, y;
622         float gausstable[2*POSTPROCESS_MAXRADIUS+1];
623         qboolean need_gauss  = (!pp.buf || pp.blur != fnt->settings->blur || pp.shadowz != fnt->settings->shadowz);
624         qboolean need_circle = (!pp.buf || pp.outline != fnt->settings->outline || pp.shadowx != fnt->settings->shadowx || pp.shadowy != fnt->settings->shadowy);
625         pp.blur = fnt->settings->blur;
626         pp.outline = fnt->settings->outline;
627         pp.shadowx = fnt->settings->shadowx;
628         pp.shadowy = fnt->settings->shadowy;
629         pp.shadowz = fnt->settings->shadowz;
630         pp.outlinepadding_l = bound(0, ceil(pp.outline - pp.shadowx), POSTPROCESS_MAXRADIUS);
631         pp.outlinepadding_r = bound(0, ceil(pp.outline + pp.shadowx), POSTPROCESS_MAXRADIUS);
632         pp.outlinepadding_t = bound(0, ceil(pp.outline - pp.shadowy), POSTPROCESS_MAXRADIUS);
633         pp.outlinepadding_b = bound(0, ceil(pp.outline + pp.shadowy), POSTPROCESS_MAXRADIUS);
634         pp.blurpadding_lt = bound(0, ceil(pp.blur - pp.shadowz), POSTPROCESS_MAXRADIUS);
635         pp.blurpadding_rb = bound(0, ceil(pp.blur + pp.shadowz), POSTPROCESS_MAXRADIUS);
636         pp.padding_l = pp.blurpadding_lt + pp.outlinepadding_l;
637         pp.padding_r = pp.blurpadding_rb + pp.outlinepadding_r;
638         pp.padding_t = pp.blurpadding_lt + pp.outlinepadding_t;
639         pp.padding_b = pp.blurpadding_rb + pp.outlinepadding_b;
640         if(need_gauss)
641         {
642                 float sum = 0;
643                 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
644                         gausstable[POSTPROCESS_MAXRADIUS+x] = (pp.blur > 0 ? exp(-(pow(x + pp.shadowz, 2))/(pp.blur*pp.blur * 2)) : (floor(x + pp.shadowz + 0.5) == 0));
645                 for(x = -pp.blurpadding_rb; x <= pp.blurpadding_lt; ++x)
646                         sum += gausstable[POSTPROCESS_MAXRADIUS+x];
647                 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
648                         pp.gausstable[POSTPROCESS_MAXRADIUS+x] = floor(gausstable[POSTPROCESS_MAXRADIUS+x] / sum * 255 + 0.5);
649         }
650         if(need_circle)
651         {
652                 for(y = -POSTPROCESS_MAXRADIUS; y <= POSTPROCESS_MAXRADIUS; ++y)
653                         for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
654                         {
655                                 float d = pp.outline + 1 - sqrt(pow(x + pp.shadowx, 2) + pow(y + pp.shadowy, 2));
656                                 pp.circlematrix[POSTPROCESS_MAXRADIUS+y][POSTPROCESS_MAXRADIUS+x] = (d >= 1) ? 255 : (d <= 0) ? 0 : floor(d * 255 + 0.5);
657                         }
658         }
659         pp.bufwidth = w + pp.padding_l + pp.padding_r;
660         pp.bufheight = h + pp.padding_t + pp.padding_b;
661         pp.bufpitch = pp.bufwidth;
662         needed = pp.bufwidth * pp.bufheight;
663         if(!pp.buf || pp.bufsize < needed * 2)
664         {
665                 if(pp.buf)
666                         Mem_Free(pp.buf);
667                 pp.bufsize = needed * 4;
668                 pp.buf = (unsigned char *)Mem_Alloc(font_mempool, pp.bufsize);
669                 pp.buf2 = pp.buf + needed;
670         }
671 }
672
673 void Font_Postprocess(ft2_font_t *fnt, unsigned char *imagedata, int pitch, int bpp, int w, int h, int *pad_l, int *pad_r, int *pad_t, int *pad_b)
674 {
675         int x, y;
676         Font_Postprocess_Update(fnt, bpp, w, h);
677         if(imagedata)
678         {
679                 // enlarge buffer
680
681                 // perform operation, not exceeding the passed padding values,
682                 // but possibly reducing them
683                 *pad_l = min(*pad_l, pp.padding_l);
684                 *pad_r = min(*pad_r, pp.padding_r);
685                 *pad_t = min(*pad_t, pp.padding_t);
686                 *pad_b = min(*pad_b, pp.padding_b);
687
688                 // calculate gauss table
689                 
690                 // outline the font (RGBA only)
691                 if(bpp == 4 && (pp.outline > 0 || pp.blur > 0 || pp.shadowx != 0 || pp.shadowy != 0 || pp.shadowz != 0)) // we can only do this in BGRA
692                 {
693                         // this is like mplayer subtitle rendering
694                         // bbuffer, bitmap buffer: this is our font
695                         // abuffer, alpha buffer: this is pp.buf
696                         // tmp: this is pp.buf2
697
698                         // create outline buffer
699                         memset(pp.buf, 0, pp.bufwidth * pp.bufheight);
700                         for(y = -*pad_t; y < h + *pad_b; ++y)
701                                 for(x = -*pad_l; x < w + *pad_r; ++x)
702                                 {
703                                         int x1 = max(-x, -pp.outlinepadding_r);
704                                         int y1 = max(-y, -pp.outlinepadding_b);
705                                         int x2 = min(pp.outlinepadding_l, w-1-x);
706                                         int y2 = min(pp.outlinepadding_t, h-1-y);
707                                         int mx, my;
708                                         int cur = 0;
709                                         int highest = 0;
710                                         for(my = y1; my <= y2; ++my)
711                                                 for(mx = x1; mx <= x2; ++mx)
712                                                 {
713                                                         cur = pp.circlematrix[POSTPROCESS_MAXRADIUS+my][POSTPROCESS_MAXRADIUS+mx] * (int)imagedata[(x+mx) * bpp + pitch * (y+my) + (bpp - 1)];
714                                                         if(cur > highest)
715                                                                 highest = cur;
716                                                 }
717                                         pp.buf[((x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t))] = (highest + 128) / 255;
718                                 }
719
720                         // blur the outline buffer
721                         if(pp.blur > 0 || pp.shadowz != 0)
722                         {
723                                 // horizontal blur
724                                 for(y = 0; y < pp.bufheight; ++y)
725                                         for(x = 0; x < pp.bufwidth; ++x)
726                                         {
727                                                 int x1 = max(-x, -pp.blurpadding_rb);
728                                                 int x2 = min(pp.blurpadding_lt, pp.bufwidth-1-x);
729                                                 int mx;
730                                                 int blurred = 0;
731                                                 for(mx = x1; mx <= x2; ++mx)
732                                                         blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+mx] * (int)pp.buf[(x+mx) + pp.bufpitch * y];
733                                                 pp.buf2[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
734                                         }
735
736                                 // vertical blur
737                                 for(y = 0; y < pp.bufheight; ++y)
738                                         for(x = 0; x < pp.bufwidth; ++x)
739                                         {
740                                                 int y1 = max(-y, -pp.blurpadding_rb);
741                                                 int y2 = min(pp.blurpadding_lt, pp.bufheight-1-y);
742                                                 int my;
743                                                 int blurred = 0;
744                                                 for(my = y1; my <= y2; ++my)
745                                                         blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+my] * (int)pp.buf2[x + pp.bufpitch * (y+my)];
746                                                 pp.buf[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
747                                         }
748                         }
749
750                         // paste the outline below the font
751                         for(y = -*pad_t; y < h + *pad_b; ++y)
752                                 for(x = -*pad_l; x < w + *pad_r; ++x)
753                                 {
754                                         unsigned char outlinealpha = pp.buf[(x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t)];
755                                         if(outlinealpha > 0)
756                                         {
757                                                 unsigned char oldalpha = imagedata[x * bpp + pitch * y + (bpp - 1)];
758                                                 // a' = 1 - (1 - a1) (1 - a2)
759                                                 unsigned char newalpha = 255 - ((255 - (int)outlinealpha) * (255 - (int)oldalpha)) / 255; // this is >= oldalpha
760                                                 // c' = (a2 c2 - a1 a2 c1 + a1 c1) / a' = (a2 c2 + a1 (1 - a2) c1) / a'
761                                                 unsigned char oldfactor     = (255 * (int)oldalpha) / newalpha;
762                                                 //unsigned char outlinefactor = ((255 - oldalpha) * (int)outlinealpha) / newalpha;
763                                                 int i;
764                                                 for(i = 0; i < bpp-1; ++i)
765                                                 {
766                                                         unsigned char c = imagedata[x * bpp + pitch * y + i];
767                                                         c = (c * (int)oldfactor) / 255 /* + outlinecolor[i] * (int)outlinefactor */;
768                                                         imagedata[x * bpp + pitch * y + i] = c;
769                                                 }
770                                                 imagedata[x * bpp + pitch * y + (bpp - 1)] = newalpha;
771                                         }
772                                         //imagedata[x * bpp + pitch * y + (bpp - 1)] |= 0x80;
773                                 }
774                 }
775         }
776         else
777         {
778                 // just calculate parameters
779                 *pad_l = pp.padding_l;
780                 *pad_r = pp.padding_r;
781                 *pad_t = pp.padding_t;
782                 *pad_b = pp.padding_b;
783         }
784 }
785
786 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size);
787 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
788 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only)
789 {
790         int map_index;
791         ft2_font_map_t *fmap, temp;
792         int gpad_l, gpad_r, gpad_t, gpad_b;
793
794         if (!(size > 0.001f && size < 1000.0f))
795                 size = 0;
796
797         if (!size)
798                 size = 16;
799         if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
800                 return false;
801
802         for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
803         {
804                 if (!font->font_maps[map_index])
805                         break;
806                 // if a similar size has already been loaded, ignore this one
807                 //abs(font->font_maps[map_index]->size - size) < 4
808                 if (font->font_maps[map_index]->size == size)
809                         return true;
810         }
811
812         if (map_index >= MAX_FONT_SIZES)
813                 return false;
814
815         if (check_only) {
816                 FT_Face fontface;
817                 if (font->image_font)
818                         fontface = (FT_Face)font->next->face;
819                 else
820                         fontface = (FT_Face)font->face;
821                 return (Font_SearchSize(font, fontface, size) > 0);
822         }
823
824         Font_Postprocess(font, NULL, 0, 4, size*2, size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
825
826         memset(&temp, 0, sizeof(temp));
827         temp.size = size;
828         temp.glyphSize = CeilPowerOf2(size*2 + max(gpad_l + gpad_r, gpad_t + gpad_b));
829         temp.sfx = (1.0/64.0)/(double)size;
830         temp.sfy = (1.0/64.0)/(double)size;
831         temp.intSize = -1; // negative value: LoadMap must search now :)
832         if (!Font_LoadMap(font, &temp, 0, &fmap))
833         {
834                 Con_Printf("ERROR: can't load the first character map for %s\n"
835                            "This is fatal\n",
836                            font->name);
837                 Font_UnloadFont(font);
838                 return false;
839         }
840         font->font_maps[map_index] = temp.next;
841
842         fmap->sfx = temp.sfx;
843         fmap->sfy = temp.sfy;
844
845         // load the default kerning vector:
846         if (font->has_kerning)
847         {
848                 Uchar l, r;
849                 FT_Vector kernvec;
850                 for (l = 0; l < 256; ++l)
851                 {
852                         for (r = 0; r < 256; ++r)
853                         {
854                                 FT_ULong ul, ur;
855                                 ul = qFT_Get_Char_Index((FT_Face)font->face, l);
856                                 ur = qFT_Get_Char_Index((FT_Face)font->face, r);
857                                 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
858                                 {
859                                         fmap->kerning.kerning[l][r][0] = 0;
860                                         fmap->kerning.kerning[l][r][1] = 0;
861                                 }
862                                 else
863                                 {
864                                         fmap->kerning.kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
865                                         fmap->kerning.kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
866                                 }
867                         }
868                 }
869         }
870         return true;
871 }
872
873 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
874 {
875         int match = -1;
876         float value = 1000000;
877         float nval;
878         int matchsize = -10000;
879         int m;
880         float fsize_x, fsize_y;
881         ft2_font_map_t **maps = font->font_maps;
882
883         fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value;
884         if(outw && *outw)
885                 fsize_x = *outw * vid.width / vid_conwidth.value;
886         if(outh && *outh)
887                 fsize_y = *outh * vid.height / vid_conheight.value;
888
889         if (fsize_x < 0)
890         {
891                 if(fsize_y < 0)
892                         fsize_x = fsize_y = 16;
893                 else
894                         fsize_x = fsize_y;
895         }
896         else
897         {
898                 if(fsize_y < 0)
899                         fsize_y = fsize_x;
900         }
901
902         for (m = 0; m < MAX_FONT_SIZES; ++m)
903         {
904                 if (!maps[m])
905                         continue;
906                 // "round up" to the bigger size if two equally-valued matches exist
907                 nval = 0.5 * (fabs(maps[m]->size - fsize_x) + fabs(maps[m]->size - fsize_y));
908                 if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
909                 {
910                         value = nval;
911                         match = m;
912                         matchsize = maps[m]->size;
913                         if (value == 0) // there is no better match
914                                 break;
915                 }
916         }
917         if (value <= r_font_size_snapping.value)
918         {
919                 // do NOT keep the aspect for perfect rendering
920                 if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
921                 if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width;
922         }
923         return match;
924 }
925
926 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
927 {
928         if (index < 0 || index >= MAX_FONT_SIZES)
929                 return NULL;
930         return font->font_maps[index];
931 }
932
933 static qboolean Font_SetSize(ft2_font_t *font, float w, float h)
934 {
935         if (font->currenth == h &&
936             ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
937              font->currentw == w)) // same size has been requested
938         {
939                 return true;
940         }
941         // sorry, but freetype doesn't seem to care about other sizes
942         w = (int)w;
943         h = (int)h;
944         if (font->image_font)
945         {
946                 if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
947                         return false;
948         }
949         else
950         {
951                 if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
952                         return false;
953         }
954         font->currentw = w;
955         font->currenth = h;
956         return true;
957 }
958
959 qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
960 {
961         ft2_font_map_t *fmap;
962         if (!font->has_kerning || !r_font_kerning.integer)
963                 return false;
964         if (map_index < 0 || map_index >= MAX_FONT_SIZES)
965                 return false;
966         fmap = font->font_maps[map_index];
967         if (!fmap)
968                 return false;
969         if (left < 256 && right < 256)
970         {
971                 //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
972                 // quick-kerning, be aware of the size: scale it
973                 if (outx) *outx = fmap->kerning.kerning[left][right][0];// * (w / (float)fmap->size);
974                 if (outy) *outy = fmap->kerning.kerning[left][right][1];// * (h / (float)fmap->size);
975                 return true;
976         }
977         else
978         {
979                 FT_Vector kernvec;
980                 FT_ULong ul, ur;
981
982                 //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
983 #if 0
984                 if (!Font_SetSize(font, w, h))
985                 {
986                         // this deserves an error message
987                         Con_Printf("Failed to get kerning for %s\n", font->name);
988                         return false;
989                 }
990                 ul = qFT_Get_Char_Index(font->face, left);
991                 ur = qFT_Get_Char_Index(font->face, right);
992                 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
993                 {
994                         if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);
995                         if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);
996                         return true;
997                 }
998 #endif
999                 if (!Font_SetSize(font, fmap->intSize, fmap->intSize))
1000                 {
1001                         // this deserves an error message
1002                         Con_Printf("Failed to get kerning for %s\n", font->name);
1003                         return false;
1004                 }
1005                 ul = qFT_Get_Char_Index((FT_Face)font->face, left);
1006                 ur = qFT_Get_Char_Index((FT_Face)font->face, right);
1007                 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
1008                 {
1009                         if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);// * (w / (float)fmap->size);
1010                         if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);// * (h / (float)fmap->size);
1011                         return true;
1012                 }
1013                 return false;
1014         }
1015 }
1016
1017 qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
1018 {
1019         return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
1020 }
1021
1022 static void UnloadMapRec(ft2_font_map_t *map)
1023 {
1024         if (map->texture)
1025         {
1026                 R_FreeTexture(map->texture);
1027                 map->texture = NULL;
1028         }
1029         if (map->next)
1030                 UnloadMapRec(map->next);
1031         Mem_Free(map);
1032 }
1033
1034 void Font_UnloadFont(ft2_font_t *font)
1035 {
1036         int i;
1037
1038         // unload fallbacks
1039         if(font->next)
1040                 Font_UnloadFont(font->next);
1041
1042         if (font->attachments && font->attachmentcount)
1043         {
1044                 for (i = 0; i < (int)font->attachmentcount; ++i) {
1045                         if (font->attachments[i].data)
1046                                 fontfilecache_Free(font->attachments[i].data);
1047                 }
1048                 Mem_Free(font->attachments);
1049                 font->attachmentcount = 0;
1050                 font->attachments = NULL;
1051         }
1052         for (i = 0; i < MAX_FONT_SIZES; ++i)
1053         {
1054                 if (font->font_maps[i])
1055                 {
1056                         UnloadMapRec(font->font_maps[i]);
1057                         font->font_maps[i] = NULL;
1058                 }
1059         }
1060         if (ft2_dll)
1061         {
1062                 if (font->face)
1063                 {
1064                         qFT_Done_Face((FT_Face)font->face);
1065                         font->face = NULL;
1066                 }
1067         }
1068         if (font->data) {
1069             fontfilecache_Free(font->data);
1070             font->data = NULL;
1071         }
1072 }
1073
1074 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size)
1075 {
1076         float intSize = size;
1077         while (1)
1078         {
1079                 if (!Font_SetSize(font, intSize, intSize))
1080                 {
1081                         Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, size, intSize);
1082                         return -1;
1083                 }
1084                 if ((fontface->size->metrics.height>>6) <= size)
1085                         return intSize;
1086                 if (intSize < 2)
1087                 {
1088                         Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, size);
1089                         return -1;
1090                 }
1091                 --intSize;
1092         }
1093 }
1094
1095 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
1096 {
1097         char map_identifier[MAX_QPATH];
1098         unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
1099         unsigned char *data;
1100         FT_ULong ch, mapch;
1101         int status;
1102         int tp;
1103         FT_Int32 load_flags;
1104         int gpad_l, gpad_r, gpad_t, gpad_b;
1105
1106         int pitch;
1107         int gR, gC; // glyph position: row and column
1108
1109         ft2_font_map_t *map, *next;
1110         ft2_font_t *usefont;
1111
1112         FT_Face fontface;
1113
1114         int bytesPerPixel = 4; // change the conversion loop too if you change this!
1115
1116         if (outmap)
1117                 *outmap = NULL;
1118
1119         if (r_font_use_alpha_textures.integer)
1120                 bytesPerPixel = 1;
1121
1122         if (font->image_font)
1123                 fontface = (FT_Face)font->next->face;
1124         else
1125                 fontface = (FT_Face)font->face;
1126
1127         switch(font->settings->antialias)
1128         {
1129                 case 0:
1130                         switch(font->settings->hinting)
1131                         {
1132                                 case 0:
1133                                         load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1134                                         break;
1135                                 case 1:
1136                                 case 2:
1137                                         load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1138                                         break;
1139                                 default:
1140                                 case 3:
1141                                         load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1142                                         break;
1143                         }
1144                         break;
1145                 default:
1146                 case 1:
1147                         switch(font->settings->hinting)
1148                         {
1149                                 case 0:
1150                                         load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1151                                         break;
1152                                 case 1:
1153                                         load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
1154                                         break;
1155                                 case 2:
1156                                         load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1157                                         break;
1158                                 default:
1159                                 case 3:
1160                                         load_flags = FT_LOAD_TARGET_NORMAL;
1161                                         break;
1162                         }
1163                         break;
1164         }
1165
1166         //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
1167         //if (status)
1168         if (font->image_font && mapstart->intSize < 0)
1169                 mapstart->intSize = mapstart->size;
1170         if (mapstart->intSize < 0)
1171         {
1172                 /*
1173                 mapstart->intSize = mapstart->size;
1174                 while (1)
1175                 {
1176                         if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1177                         {
1178                                 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
1179                                 return false;
1180                         }
1181                         if ((fontface->size->metrics.height>>6) <= mapstart->size)
1182                                 break;
1183                         if (mapstart->intSize < 2)
1184                         {
1185                                 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
1186                                 return false;
1187                         }
1188                         --mapstart->intSize;
1189                 }
1190                 */
1191                 if ((mapstart->intSize = Font_SearchSize(font, fontface, mapstart->size)) <= 0)
1192                         return false;
1193                 Con_DPrintf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
1194         }
1195
1196         if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1197         {
1198                 Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
1199                 return false;
1200         }
1201
1202         map = (ft2_font_map_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
1203         if (!map)
1204         {
1205                 Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
1206                 return false;
1207         }
1208
1209         Font_Postprocess(font, NULL, 0, bytesPerPixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
1210
1211         // copy over the information
1212         map->size = mapstart->size;
1213         map->intSize = mapstart->intSize;
1214         map->glyphSize = mapstart->glyphSize;
1215         map->sfx = mapstart->sfx;
1216         map->sfy = mapstart->sfy;
1217
1218         pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
1219         data = (unsigned char *)Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
1220         if (!data)
1221         {
1222                 Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
1223                 Mem_Free(map);
1224                 return false;
1225         }
1226         memset(map->width_of, 0, sizeof(map->width_of));
1227
1228         // initialize as white texture with zero alpha
1229         tp = 0;
1230         while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
1231         {
1232                 if (bytesPerPixel == 4)
1233                 {
1234                         data[tp++] = 0xFF;
1235                         data[tp++] = 0xFF;
1236                         data[tp++] = 0xFF;
1237                 }
1238                 data[tp++] = 0x00;
1239         }
1240
1241         // insert the map
1242         map->start = mapidx * FONT_CHARS_PER_MAP;
1243         next = mapstart;
1244         while(next->next && next->next->start < map->start)
1245                 next = next->next;
1246         map->next = next->next;
1247         next->next = map;
1248
1249         gR = 0;
1250         gC = -1;
1251         for (ch = map->start;
1252              ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
1253              ++ch)
1254         {
1255                 FT_ULong glyphIndex;
1256                 int w, h, x, y;
1257                 FT_GlyphSlot glyph;
1258                 FT_Bitmap *bmp;
1259                 unsigned char *imagedata, *dst, *src;
1260                 glyph_slot_t *mapglyph;
1261                 FT_Face face;
1262                 int pad_l, pad_r, pad_t, pad_b;
1263
1264                 mapch = ch - map->start;
1265
1266                 if (developer_font.integer)
1267                         Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
1268
1269                 ++gC;
1270                 if (gC >= FONT_CHARS_PER_LINE)
1271                 {
1272                         gC -= FONT_CHARS_PER_LINE;
1273                         ++gR;
1274                 }
1275
1276                 imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
1277                 imagedata += gpad_t * pitch + gpad_l * bytesPerPixel;
1278                 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
1279                 // we need the glyphIndex
1280                 face = (FT_Face)font->face;
1281                 usefont = NULL;
1282                 if (font->image_font && mapch == ch && img_fontmap[mapch])
1283                 {
1284                         map->glyphs[mapch].image = true;
1285                         continue;
1286                 }
1287                 glyphIndex = qFT_Get_Char_Index(face, ch);
1288                 if (glyphIndex == 0)
1289                 {
1290                         // by convention, 0 is the "missing-glyph"-glyph
1291                         // try to load from a fallback font
1292                         for(usefont = font->next; usefont != NULL; usefont = usefont->next)
1293                         {
1294                                 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
1295                                         continue;
1296                                 // try that glyph
1297                                 face = (FT_Face)usefont->face;
1298                                 glyphIndex = qFT_Get_Char_Index(face, ch);
1299                                 if (glyphIndex == 0)
1300                                         continue;
1301                                 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1302                                 if (status)
1303                                         continue;
1304                                 break;
1305                         }
1306                         if (!usefont)
1307                         {
1308                                 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1309                                 // now we let it use the "missing-glyph"-glyph
1310                                 face = (FT_Face)font->face;
1311                                 glyphIndex = 0;
1312                         }
1313                 }
1314
1315                 if (!usefont)
1316                 {
1317                         usefont = font;
1318                         face = (FT_Face)font->face;
1319                         status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1320                         if (status)
1321                         {
1322                                 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1323                                 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1324                                 continue;
1325                         }
1326                 }
1327
1328                 glyph = face->glyph;
1329                 bmp = &glyph->bitmap;
1330
1331                 w = bmp->width;
1332                 h = bmp->rows;
1333
1334                 if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
1335                         Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
1336                         if (w > map->glyphSize)
1337                                 w = map->glyphSize - gpad_l - gpad_r;
1338                         if (h > map->glyphSize)
1339                                 h = map->glyphSize;
1340                 }
1341
1342                 switch (bmp->pixel_mode)
1343                 {
1344                 case FT_PIXEL_MODE_MONO:
1345                         if (developer_font.integer)
1346                                 Con_DPrint("glyphinfo:   Pixel Mode: MONO\n");
1347                         break;
1348                 case FT_PIXEL_MODE_GRAY2:
1349                         if (developer_font.integer)
1350                                 Con_DPrint("glyphinfo:   Pixel Mode: GRAY2\n");
1351                         break;
1352                 case FT_PIXEL_MODE_GRAY4:
1353                         if (developer_font.integer)
1354                                 Con_DPrint("glyphinfo:   Pixel Mode: GRAY4\n");
1355                         break;
1356                 case FT_PIXEL_MODE_GRAY:
1357                         if (developer_font.integer)
1358                                 Con_DPrint("glyphinfo:   Pixel Mode: GRAY\n");
1359                         break;
1360                 default:
1361                         if (developer_font.integer)
1362                                 Con_DPrintf("glyphinfo:   Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1363                         Mem_Free(data);
1364                         Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1365                         return false;
1366                 }
1367                 for (y = 0; y < h; ++y)
1368                 {
1369                         dst = imagedata + y * pitch;
1370                         src = bmp->buffer + y * bmp->pitch;
1371
1372                         switch (bmp->pixel_mode)
1373                         {
1374                         case FT_PIXEL_MODE_MONO:
1375                                 dst += bytesPerPixel - 1; // shift to alpha byte
1376                                 for (x = 0; x < bmp->width; x += 8)
1377                                 {
1378                                         unsigned char ch = *src++;
1379                                         *dst = 255 * !!((ch & 0x80) >> 7); dst += bytesPerPixel;
1380                                         *dst = 255 * !!((ch & 0x40) >> 6); dst += bytesPerPixel;
1381                                         *dst = 255 * !!((ch & 0x20) >> 5); dst += bytesPerPixel;
1382                                         *dst = 255 * !!((ch & 0x10) >> 4); dst += bytesPerPixel;
1383                                         *dst = 255 * !!((ch & 0x08) >> 3); dst += bytesPerPixel;
1384                                         *dst = 255 * !!((ch & 0x04) >> 2); dst += bytesPerPixel;
1385                                         *dst = 255 * !!((ch & 0x02) >> 1); dst += bytesPerPixel;
1386                                         *dst = 255 * !!((ch & 0x01) >> 0); dst += bytesPerPixel;
1387                                 }
1388                                 break;
1389                         case FT_PIXEL_MODE_GRAY2:
1390                                 dst += bytesPerPixel - 1; // shift to alpha byte
1391                                 for (x = 0; x < bmp->width; x += 4)
1392                                 {
1393                                         unsigned char ch = *src++;
1394                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1395                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1396                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1397                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1398                                 }
1399                                 break;
1400                         case FT_PIXEL_MODE_GRAY4:
1401                                 dst += bytesPerPixel - 1; // shift to alpha byte
1402                                 for (x = 0; x < bmp->width; x += 2)
1403                                 {
1404                                         unsigned char ch = *src++;
1405                                         *dst = ( ((ch & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
1406                                         *dst = ( ((ch & 0x0F) ) * 0x11); dst += bytesPerPixel;
1407                                 }
1408                                 break;
1409                         case FT_PIXEL_MODE_GRAY:
1410                                 // in this case pitch should equal width
1411                                 for (tp = 0; tp < bmp->pitch; ++tp)
1412                                         dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
1413
1414                                 //memcpy((void*)dst, (void*)src, bmp->pitch);
1415                                 //dst += bmp->pitch;
1416                                 break;
1417                         default:
1418                                 break;
1419                         }
1420                 }
1421
1422                 pad_l = gpad_l;
1423                 pad_r = gpad_r;
1424                 pad_t = gpad_t;
1425                 pad_b = gpad_b;
1426                 Font_Postprocess(font, imagedata, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1427
1428                 // now fill map->glyphs[ch - map->start]
1429                 mapglyph = &map->glyphs[mapch];
1430
1431                 {
1432                         // old way
1433                         // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1434
1435                         double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1436                         //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1437                         double advance = (glyph->advance.x / 64.0) / map->size;
1438                         //double mWidth = (glyph->metrics.width >> 6) / map->size;
1439                         //double mHeight = (glyph->metrics.height >> 6) / map->size;
1440
1441                         mapglyph->txmin = ( (double)(gC * map->glyphSize) + (double)(gpad_l - pad_l) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1442                         mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1443                         mapglyph->tymin = ( (double)(gR * map->glyphSize) + (double)(gpad_r - pad_r) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1444                         mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1445                         //mapglyph->vxmin = bearingX;
1446                         //mapglyph->vxmax = bearingX + mWidth;
1447                         mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
1448                         mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
1449                         //mapglyph->vymin = -bearingY;
1450                         //mapglyph->vymax = mHeight - bearingY;
1451                         mapglyph->vymin = (-glyph->bitmap_top - pad_t) / map->size;
1452                         mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
1453                         //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);
1454                         //mapglyph->advance_x = advance * usefont->size;
1455                         //mapglyph->advance_x = advance;
1456                         mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1457                         mapglyph->advance_y = 0;
1458
1459                         if (developer_font.integer)
1460                         {
1461                                 Con_DPrintf("glyphinfo:   Glyph: %lu   at (%i, %i)\n", (unsigned long)ch, gC, gR);
1462                                 Con_DPrintf("glyphinfo:   %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1463                                 if (ch >= 32 && ch <= 128)
1464                                         Con_DPrintf("glyphinfo:   Character: %c\n", (int)ch);
1465                                 Con_DPrintf("glyphinfo:   Vertex info:\n");
1466                                 Con_DPrintf("glyphinfo:     X: ( %f  --  %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1467                                 Con_DPrintf("glyphinfo:     Y: ( %f  --  %f )\n", mapglyph->vymin, mapglyph->vymax);
1468                                 Con_DPrintf("glyphinfo:   Texture info:\n");
1469                                 Con_DPrintf("glyphinfo:     S: ( %f  --  %f )\n", mapglyph->txmin, mapglyph->txmax);
1470                                 Con_DPrintf("glyphinfo:     T: ( %f  --  %f )\n", mapglyph->tymin, mapglyph->tymax);
1471                                 Con_DPrintf("glyphinfo:   Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1472                         }
1473                 }
1474                 map->glyphs[mapch].image = false;
1475         }
1476
1477         // create a texture from the data now
1478
1479         if (developer_font.integer > 100)
1480         {
1481                 // LordHavoc: why are we writing this?  And why not write it as TGA using the appropriate function?
1482                 // view using `display -depth 8 -size 512x512 name_page.rgba` (be sure to use a correct -size parameter)
1483                 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u.rgba", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1484                 FS_WriteFile(map_identifier, data, pitch * FONT_CHAR_LINES * map->glyphSize);
1485         }
1486         dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1487
1488         // probably use bytesPerPixel here instead?
1489         if (r_font_use_alpha_textures.integer)
1490         {
1491                 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1492                                                map->glyphSize * FONT_CHARS_PER_LINE,
1493                                                map->glyphSize * FONT_CHAR_LINES,
1494                                                data, TEXTYPE_ALPHA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, -1, NULL);
1495         } else {
1496                 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1497                                                map->glyphSize * FONT_CHARS_PER_LINE,
1498                                                map->glyphSize * FONT_CHAR_LINES,
1499                                                data, TEXTYPE_RGBA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, -1, NULL);
1500         }
1501
1502         Mem_Free(data);
1503         if (!map->texture)
1504         {
1505                 // if the first try isn't successful, keep it with a broken texture
1506                 // otherwise we retry to load it every single frame where ft2 rendering is used
1507                 // this would be bad...
1508                 // only `data' must be freed
1509                 Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
1510                            font->name, mapstart->size, mapidx);
1511                 return false;
1512         }
1513         if (outmap)
1514                 *outmap = map;
1515         return true;
1516 }
1517
1518 qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
1519 {
1520         if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1521                 return false;
1522         // the first map must have been loaded already
1523         if (!font->font_maps[map_index])
1524                 return false;
1525         return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
1526 }
1527
1528 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1529 {
1530         while (start && start->start + FONT_CHARS_PER_MAP <= ch)
1531                 start = start->next;
1532         if (start && start->start > ch)
1533                 return NULL;
1534         return start;
1535 }