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