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