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