]> icculus.org git repositories - divverent/darkplaces.git/blob - ft2.c
do not TOTALLY block translation with csprogs, but restrict it to globals named dotra...
[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
39 /*
40 ================================================================================
41 Function definitions. Taken from the freetype2 headers.
42 ================================================================================
43 */
44
45
46 FT_EXPORT( FT_Error )
47 (*qFT_Init_FreeType)( FT_Library  *alibrary );
48 FT_EXPORT( FT_Error )
49 (*qFT_Done_FreeType)( FT_Library  library );
50 /*
51 FT_EXPORT( FT_Error )
52 (*qFT_New_Face)( FT_Library   library,
53                  const char*  filepathname,
54                  FT_Long      face_index,
55                  FT_Face     *aface );
56 */
57 FT_EXPORT( FT_Error )
58 (*qFT_New_Memory_Face)( FT_Library      library,
59                         const FT_Byte*  file_base,
60                         FT_Long         file_size,
61                         FT_Long         face_index,
62                         FT_Face        *aface );
63 FT_EXPORT( FT_Error )
64 (*qFT_Done_Face)( FT_Face  face );
65 FT_EXPORT( FT_Error )
66 (*qFT_Select_Size)( FT_Face  face,
67                     FT_Int   strike_index );
68 FT_EXPORT( FT_Error )
69 (*qFT_Request_Size)( FT_Face          face,
70                      FT_Size_Request  req );
71 FT_EXPORT( FT_Error )
72 (*qFT_Set_Char_Size)( FT_Face     face,
73                       FT_F26Dot6  char_width,
74                       FT_F26Dot6  char_height,
75                       FT_UInt     horz_resolution,
76                       FT_UInt     vert_resolution );
77 FT_EXPORT( FT_Error )
78 (*qFT_Set_Pixel_Sizes)( FT_Face  face,
79                         FT_UInt  pixel_width,
80                         FT_UInt  pixel_height );
81 FT_EXPORT( FT_Error )
82 (*qFT_Load_Glyph)( FT_Face   face,
83                    FT_UInt   glyph_index,
84                    FT_Int32  load_flags );
85 FT_EXPORT( FT_Error )
86 (*qFT_Load_Char)( FT_Face   face,
87                   FT_ULong  char_code,
88                   FT_Int32  load_flags );
89 FT_EXPORT( FT_UInt )
90 (*qFT_Get_Char_Index)( FT_Face   face,
91                        FT_ULong  charcode );
92 FT_EXPORT( FT_Error )
93 (*qFT_Render_Glyph)( FT_GlyphSlot    slot,
94                      FT_Render_Mode  render_mode );
95 FT_EXPORT( FT_Error )
96 (*qFT_Get_Kerning)( FT_Face     face,
97                     FT_UInt     left_glyph,
98                     FT_UInt     right_glyph,
99                     FT_UInt     kern_mode,
100                     FT_Vector  *akerning );
101 FT_EXPORT( FT_Error )
102 (*qFT_Attach_Stream)( FT_Face        face,
103                       FT_Open_Args*  parameters );
104 /*
105 ================================================================================
106 Support for dynamically loading the FreeType2 library
107 ================================================================================
108 */
109
110 static dllfunction_t ft2funcs[] =
111 {
112         {"FT_Init_FreeType",            (void **) &qFT_Init_FreeType},
113         {"FT_Done_FreeType",            (void **) &qFT_Done_FreeType},
114         //{"FT_New_Face",                       (void **) &qFT_New_Face},
115         {"FT_New_Memory_Face",          (void **) &qFT_New_Memory_Face},
116         {"FT_Done_Face",                (void **) &qFT_Done_Face},
117         {"FT_Select_Size",              (void **) &qFT_Select_Size},
118         {"FT_Request_Size",             (void **) &qFT_Request_Size},
119         {"FT_Set_Char_Size",            (void **) &qFT_Set_Char_Size},
120         {"FT_Set_Pixel_Sizes",          (void **) &qFT_Set_Pixel_Sizes},
121         {"FT_Load_Glyph",               (void **) &qFT_Load_Glyph},
122         {"FT_Load_Char",                (void **) &qFT_Load_Char},
123         {"FT_Get_Char_Index",           (void **) &qFT_Get_Char_Index},
124         {"FT_Render_Glyph",             (void **) &qFT_Render_Glyph},
125         {"FT_Get_Kerning",              (void **) &qFT_Get_Kerning},
126         {"FT_Attach_Stream",            (void **) &qFT_Attach_Stream},
127         {NULL, NULL}
128 };
129
130 /// Handle for FreeType2 DLL
131 static dllhandle_t ft2_dll = NULL;
132
133 /// Memory pool for fonts
134 static mempool_t *font_mempool= NULL;
135 static rtexturepool_t *font_texturepool = NULL;
136
137 /// FreeType library handle
138 static FT_Library font_ft2lib = NULL;
139
140 /*
141 ====================
142 Font_CloseLibrary
143
144 Unload the FreeType2 DLL
145 ====================
146 */
147 void Font_CloseLibrary (void)
148 {
149         if (font_mempool)
150                 Mem_FreePool(&font_mempool);
151         if (font_texturepool)
152                 R_FreeTexturePool(&font_texturepool);
153         if (font_ft2lib && qFT_Done_FreeType)
154         {
155                 qFT_Done_FreeType(font_ft2lib);
156                 font_ft2lib = NULL;
157         }
158         Sys_UnloadLibrary (&ft2_dll);
159 }
160
161 /*
162 ====================
163 Font_OpenLibrary
164
165 Try to load the FreeType2 DLL
166 ====================
167 */
168 qboolean Font_OpenLibrary (void)
169 {
170         const char* dllnames [] =
171         {
172 #if defined(WIN64)
173                 #error path for freetype 2 dll
174 #elif defined(WIN32)
175                 "freetype6.dll",
176 #elif defined(MACOSX)
177                 "libfreetype.dylib",
178 #else
179                 "libfreetype.so.6",
180                 "libfreetype.so",
181 #endif
182                 NULL
183         };
184
185         if (r_font_disable_freetype.integer)
186                 return false;
187
188         // Already loaded?
189         if (ft2_dll)
190                 return true;
191
192         // Load the DLL
193         if (!Sys_LoadLibrary (dllnames, &ft2_dll, ft2funcs))
194                 return false;
195         return true;
196 }
197
198 /*
199 ====================
200 Font_Init
201
202 Initialize the freetype2 font subsystem
203 ====================
204 */
205
206 void font_start(void)
207 {
208         if (!Font_OpenLibrary())
209                 return;
210
211         if (qFT_Init_FreeType(&font_ft2lib))
212         {
213                 Con_Print("ERROR: Failed to initialize the FreeType2 library!\n");
214                 Font_CloseLibrary();
215                 return;
216         }
217
218         font_mempool = Mem_AllocPool("FONT", 0, NULL);
219         if (!font_mempool)
220         {
221                 Con_Print("ERROR: Failed to allocate FONT memory pool!\n");
222                 Font_CloseLibrary();
223                 return;
224         }
225
226         font_texturepool = R_AllocTexturePool();
227         if (!font_texturepool)
228         {
229                 Con_Print("ERROR: Failed to allocate FONT texture pool!\n");
230                 Font_CloseLibrary();
231                 return;
232         }
233 }
234
235 void font_shutdown(void)
236 {
237         int i;
238         for (i = 0; i < MAX_FONTS; ++i)
239         {
240                 if (dp_fonts[i].ft2)
241                 {
242                         Font_UnloadFont(dp_fonts[i].ft2);
243                         dp_fonts[i].ft2 = NULL;
244                 }
245         }
246         Font_CloseLibrary();
247 }
248
249 void font_newmap(void)
250 {
251 }
252
253 void Font_Init(void)
254 {
255         Cvar_RegisterVariable(&r_font_disable_freetype);
256         Cvar_RegisterVariable(&r_font_use_alpha_textures);
257         Cvar_RegisterVariable(&r_font_size_snapping);
258 }
259
260 /*
261 ================================================================================
262 Implementation of a more or less lazy font loading and rendering code.
263 ================================================================================
264 */
265
266 #include "ft2_fontdefs.h"
267
268 ft2_font_t *Font_Alloc(void)
269 {
270         if (!ft2_dll)
271                 return NULL;
272         return Mem_Alloc(font_mempool, sizeof(ft2_font_t));
273 }
274
275 qboolean Font_Attach(ft2_font_t *font, ft2_attachment_t *attachment)
276 {
277         ft2_attachment_t *na;
278
279         font->attachmentcount++;
280         na = (ft2_attachment_t*)Mem_Alloc(font_mempool, sizeof(font->attachments[0]) * font->attachmentcount);
281         if (na == NULL)
282                 return false;
283         if (font->attachments && font->attachmentcount > 1)
284         {
285                 memcpy(na, font->attachments, sizeof(font->attachments[0]) * (font->attachmentcount - 1));
286                 Mem_Free(font->attachments);
287         }
288         memcpy(na + sizeof(font->attachments[0]) * (font->attachmentcount - 1), attachment, sizeof(*attachment));
289         font->attachments = na;
290         return true;
291 }
292
293 static qboolean Font_LoadFile(const char *name, int _face, ft2_font_t *font);
294 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean no_texture, qboolean no_kerning);
295 qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt)
296 {
297         int s, count, i;
298         ft2_font_t *ft2, *fbfont, *fb;
299
300         ft2 = Font_Alloc();
301         if (!ft2)
302         {
303                 dpfnt->ft2 = NULL;
304                 return false;
305         }
306
307         // check if a fallback font has been specified, if it has been, and the
308         // font fails to load, use the image font as main font
309         for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
310         {
311                 if (dpfnt->fallbacks[i][0])
312                         break;
313         }
314
315         if (!Font_LoadFile(name, dpfnt->req_face, ft2))
316         {
317                 if (i >= MAX_FONT_FALLBACKS)
318                 {
319                         dpfnt->ft2 = NULL;
320                         Mem_Free(ft2);
321                         return false;
322                 }
323                 strlcpy(ft2->name, name, sizeof(ft2->name));
324                 ft2->image_font = true;
325                 ft2->has_kerning = false;
326         }
327         else
328         {
329                 ft2->image_font = false;
330         }
331
332         // attempt to load fallback fonts:
333         fbfont = ft2;
334         for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
335         {
336                 if (!dpfnt->fallbacks[i][0])
337                         break;
338                 if (! (fb = Font_Alloc()) )
339                 {
340                         Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
341                         break;
342                 }
343                 if (!Font_LoadFile(dpfnt->fallbacks[i], dpfnt->fallback_faces[i], fb))
344                 {
345                         Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
346                         Mem_Free(fb);
347                         break;
348                 }
349                 count = 0;
350                 for (s = 0; s < MAX_FONT_SIZES; ++s)
351                 {
352                         if (Font_LoadSize(fb, dpfnt->req_sizes[s], true, false))
353                                 ++count;
354                 }
355                 if (!count)
356                 {
357                         Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
358                         Font_UnloadFont(fb);
359                         Mem_Free(fb);
360                         break;
361                 }
362                 // at least one size of the fallback font loaded successfully
363                 // link it:
364                 fbfont->next = fb;
365                 fbfont = fb;
366         }
367
368         if (fbfont == ft2 && ft2->image_font)
369         {
370                 // no fallbacks were loaded successfully:
371                 dpfnt->ft2 = NULL;
372                 Mem_Free(ft2);
373                 return false;
374         }
375
376         count = 0;
377         for (s = 0; s < MAX_FONT_SIZES; ++s)
378         {
379                 if (Font_LoadSize(ft2, dpfnt->req_sizes[s], false, false))
380                         ++count;
381         }
382         if (!count)
383         {
384                 // loading failed for every requested size
385                 Font_UnloadFont(ft2);
386                 Mem_Free(ft2);
387                 dpfnt->ft2 = NULL;
388                 return false;
389         }
390
391         //Con_Printf("%i sizes loaded\n", count);
392         dpfnt->ft2 = ft2;
393         return true;
394 }
395
396 static qboolean Font_LoadFile(const char *name, int _face, ft2_font_t *font)
397 {
398         size_t namelen;
399         char filename[MAX_QPATH];
400         int status;
401         size_t i;
402         unsigned char *data;
403         fs_offset_t datasize;
404
405         memset(font, 0, sizeof(*font));
406
407         if (!Font_OpenLibrary())
408         {
409                 if (!r_font_disable_freetype.integer)
410                 {
411                         Con_Printf("WARNING: can't open load font %s\n"
412                                    "You need the FreeType2 DLL to load font files\n",
413                                    name);
414                 }
415                 return false;
416         }
417
418         namelen = strlen(name);
419
420         memcpy(filename, name, namelen);
421         memcpy(filename + namelen, ".ttf", 5);
422         data = FS_LoadFile(filename, font_mempool, false, &datasize);
423         if (!data)
424         {
425                 memcpy(filename + namelen, ".otf", 5);
426                 data = FS_LoadFile(filename, font_mempool, false, &datasize);
427         }
428         if (!data)
429         {
430                 ft2_attachment_t afm;
431
432                 memcpy(filename + namelen, ".pfb", 5);
433                 data = FS_LoadFile(filename, font_mempool, false, &datasize);
434
435                 if (data)
436                 {
437                         memcpy(filename + namelen, ".afm", 5);
438                         afm.data = FS_LoadFile(filename, font_mempool, false, &afm.size);
439
440                         if (afm.data)
441                                 Font_Attach(font, &afm);
442                 }
443         }
444
445         if (!data)
446         {
447                 // FS_LoadFile being not-quiet should print an error :)
448                 return false;
449         }
450         Con_Printf("Loading font %s face %i...\n", filename, _face);
451
452         status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
453         if (status && _face != 0)
454         {
455                 Con_Printf("Failed to load face %i of %s. Falling back to face 0\n", _face, name);
456                 _face = 0;
457                 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, 0, (FT_Face*)&font->face);
458         }
459         if (status)
460         {
461                 Con_Printf("ERROR: can't create face for %s\n"
462                            "Error %i\n", // TODO: error strings
463                            name, status);
464                 Font_UnloadFont(font);
465                 return false;
466         }
467
468         // add the attachments
469         for (i = 0; i < font->attachmentcount; ++i)
470         {
471                 FT_Open_Args args;
472                 memset(&args, 0, sizeof(args));
473                 args.flags = FT_OPEN_MEMORY;
474                 args.memory_base = (const FT_Byte*)font->attachments[i].data;
475                 args.memory_size = font->attachments[i].size;
476                 if (qFT_Attach_Stream(font->face, &args))
477                         Con_Printf("Failed to add attachment %u to %s\n", (unsigned)i, font->name);
478         }
479
480         memcpy(font->name, name, namelen+1);
481         font->image_font = false;
482         font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
483         return true;
484 }
485
486 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
487 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean no_texture, qboolean no_kerning)
488 {
489         int map_index;
490         ft2_font_map_t *fmap, temp;
491
492         if (IS_NAN(size))
493                 size = 0;
494
495         if (!size)
496                 size = 16;
497         if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
498                 return false;
499
500         if (!no_texture)
501         {
502                 for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
503                 {
504                         if (!font->font_maps[map_index])
505                                 break;
506                         // if a similar size has already been loaded, ignore this one
507                         //abs(font->font_maps[map_index]->size - size) < 4
508                         if (font->font_maps[map_index]->size == size)
509                                 return true;
510                 }
511
512                 if (map_index >= MAX_FONT_SIZES)
513                         return false;
514
515                 memset(&temp, 0, sizeof(temp));
516                 temp.size = size;
517                 temp.glyphSize = CeilPowerOf2(size*2);
518                 temp.sfx = (1.0/64.0)/(double)size;
519                 temp.sfy = (1.0/64.0)/(double)size;
520                 temp.intSize = -1; // negative value: LoadMap must search now :)
521                 if (!Font_LoadMap(font, &temp, 0, &fmap))
522                 {
523                         Con_Printf("ERROR: can't load the first character map for %s\n"
524                                    "This is fatal\n",
525                                    font->name);
526                         Font_UnloadFont(font);
527                         return false;
528                 }
529                 font->font_maps[map_index] = temp.next;
530
531                 fmap->sfx = temp.sfx;
532                 fmap->sfy = temp.sfy;
533         }
534         if (!no_kerning)
535         {
536                 // load the default kerning vector:
537                 if (font->has_kerning)
538                 {
539                         Uchar l, r;
540                         FT_Vector kernvec;
541                         for (l = 0; l < 256; ++l)
542                         {
543                                 for (r = 0; r < 256; ++r)
544                                 {
545                                         FT_ULong ul, ur;
546                                         ul = qFT_Get_Char_Index(font->face, l);
547                                         ur = qFT_Get_Char_Index(font->face, r);
548                                         if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
549                                         {
550                                                 fmap->kerning.kerning[l][r][0] = 0;
551                                                 fmap->kerning.kerning[l][r][1] = 0;
552                                         }
553                                         else
554                                         {
555                                                 fmap->kerning.kerning[l][r][0] = (kernvec.x >> 6) / fmap->size;
556                                                 fmap->kerning.kerning[l][r][1] = (kernvec.y >> 6) / fmap->size;
557                                         }
558                                 }
559                         }
560                 }
561         }
562
563         return true;
564 }
565
566 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
567 {
568         int match = -1;
569         int value = 1000000;
570         int nval;
571         int matchsize = -10000;
572         int m;
573         int size;
574         float fsize;
575         ft2_font_map_t **maps = font->font_maps;
576
577         fsize = _fsize * vid.height / vid_conheight.value;
578
579         if (fsize < 0)
580                 size = 16;
581         else
582         {
583                 // round up
584                 size = (int)fsize;
585                 if (fsize - (float)size >= 0.49)
586                         ++size;
587         }
588
589         for (m = 0; m < MAX_FONT_SIZES; ++m)
590         {
591                 if (!maps[m])
592                         continue;
593                 // "round up" to the bigger size if two equally-valued matches exist
594                 nval = abs(maps[m]->size - size);
595                 if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
596                 {
597                         value = nval;
598                         match = m;
599                         matchsize = maps[m]->size;
600                         if (value == 0) // there is no better match
601                                 break;
602                 }
603         }
604         if (value <= r_font_size_snapping.value)
605         {
606                 if (outw && outh)
607                 {
608                         if (!*outh) *outh = *outw;
609                         if (!*outw) *outw = *outh;
610                 }
611                 // keep the aspect
612                 if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
613                 if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width * *outw / _fsize;
614         }
615         return match;
616 }
617
618 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
619 {
620         if (index < 0 || index >= MAX_FONT_SIZES)
621                 return NULL;
622         return font->font_maps[index];
623 }
624
625 static qboolean Font_SetSize(ft2_font_t *font, float w, float h)
626 {
627         if (font->currenth == h &&
628             ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
629              font->currentw == w)) // same size has been requested
630         {
631                 return true;
632         }
633         // sorry, but freetype doesn't seem to care about other sizes
634         w = (int)w;
635         h = (int)h;
636         if (font->image_font)
637         {
638                 if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
639                         return false;
640         }
641         else
642         {
643                 if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
644                         return false;
645         }
646         font->currentw = w;
647         font->currenth = h;
648         return true;
649 }
650
651 qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
652 {
653         ft2_font_map_t *fmap;
654         if (!font->has_kerning)
655                 return false;
656         if (map_index < 0 || map_index >= MAX_FONT_SIZES)
657                 return false;
658         fmap = font->font_maps[map_index];
659         if (!fmap)
660                 return false;
661         if (left < 256 && right < 256)
662         {
663                 // quick-kerning, be aware of the size: scale it
664                 if (outx) *outx = fmap->kerning.kerning[left][right][0] * w / (float)fmap->size;
665                 if (outy) *outy = fmap->kerning.kerning[left][right][1] * h / (float)fmap->size;
666                 return true;
667         }
668         else
669         {
670                 FT_Vector kernvec;
671                 FT_ULong ul, ur;
672
673                 //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
674                 if (!Font_SetSize(font, w, h))
675                 {
676                         // this deserves an error message
677                         Con_Printf("Failed to get kerning for %s\n", font->name);
678                         return false;
679                 }
680                 ul = qFT_Get_Char_Index(font->face, left);
681                 ur = qFT_Get_Char_Index(font->face, right);
682                 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
683                 {
684                         if (outx) *outx = kernvec.x * fmap->sfx;
685                         if (outy) *outy = kernvec.y * fmap->sfy;
686                         return true;
687                 }
688                 return false;
689         }
690 }
691
692 qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
693 {
694         return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
695 }
696
697 static void UnloadMapRec(ft2_font_map_t *map)
698 {
699         if (map->texture)
700         {
701                 R_FreeTexture(map->texture);
702                 map->texture = NULL;
703         }
704         if (map->next)
705                 UnloadMapRec(map->next);
706         Mem_Free(map);
707 }
708
709 void Font_UnloadFont(ft2_font_t *font)
710 {
711         int i;
712         if (font->attachments && font->attachmentcount)
713         {
714                 Mem_Free(font->attachments);
715                 font->attachmentcount = 0;
716                 font->attachments = NULL;
717         }
718         for (i = 0; i < MAX_FONT_SIZES; ++i)
719         {
720                 if (font->font_maps[i])
721                 {
722                         UnloadMapRec(font->font_maps[i]);
723                         font->font_maps[i] = NULL;
724                 }
725         }
726         if (ft2_dll)
727         {
728                 if (font->face)
729                 {
730                         qFT_Done_Face((FT_Face)font->face);
731                         font->face = NULL;
732                 }
733         }
734 }
735
736 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
737 {
738         char map_identifier[MAX_QPATH];
739         unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
740         unsigned char *data;
741         FT_ULong ch, mapch;
742         int status;
743         int tp;
744
745         int pitch;
746         int gR, gC; // glyph position: row and column
747
748         ft2_font_map_t *map, *next;
749         ft2_font_t *usefont;
750
751         FT_Face fontface;
752
753         int bytesPerPixel = 4; // change the conversion loop too if you change this!
754
755         if (outmap)
756                 *outmap = NULL;
757
758         if (r_font_use_alpha_textures.integer)
759                 bytesPerPixel = 1;
760
761         if (font->image_font)
762                 fontface = (FT_Face)font->next->face;
763         else
764                 fontface = (FT_Face)font->face;
765
766         //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
767         //if (status)
768         if (font->image_font && mapstart->intSize < 0)
769                 mapstart->intSize = mapstart->size;
770         if (mapstart->intSize < 0)
771         {
772                 mapstart->intSize = mapstart->size;
773                 while (1)
774                 {
775                         if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
776                         {
777                                 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
778                                 return false;
779                         }
780                         if ((fontface->size->metrics.height>>6) <= mapstart->size)
781                                 break;
782                         if (mapstart->intSize < 2)
783                         {
784                                 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
785                                 return false;
786                         }
787                         --mapstart->intSize;
788                 }
789                 if (developer.integer)
790                         Con_Printf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
791         }
792
793         if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
794         {
795                 Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
796                 return false;
797         }
798
799         map = Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
800         if (!map)
801         {
802                 Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
803                 return false;
804         }
805
806         // copy over the information
807         map->size = mapstart->size;
808         map->intSize = mapstart->intSize;
809         map->glyphSize = mapstart->glyphSize;
810         map->sfx = mapstart->sfx;
811         map->sfy = mapstart->sfy;
812
813         pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
814         data = Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
815         if (!data)
816         {
817                 Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
818                 Mem_Free(map);
819                 return false;
820         }
821
822         // initialize as white texture with zero alpha
823         tp = 0;
824         while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
825         {
826                 if (bytesPerPixel == 4)
827                 {
828                         data[tp++] = 0xFF;
829                         data[tp++] = 0xFF;
830                         data[tp++] = 0xFF;
831                 }
832                 data[tp++] = 0x00;
833         }
834
835         // insert the map
836         map->start = mapidx * FONT_CHARS_PER_MAP;
837         next = mapstart;
838         while(next->next && next->next->start < map->start)
839                 next = next->next;
840         map->next = next->next;
841         next->next = map;
842
843         gR = 0;
844         gC = -1;
845         for (ch = map->start;
846              ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
847              ++ch)
848         {
849                 FT_ULong glyphIndex;
850                 int w, h, x, y;
851                 FT_GlyphSlot glyph;
852                 FT_Bitmap *bmp;
853                 unsigned char *imagedata, *dst, *src;
854                 glyph_slot_t *mapglyph;
855                 FT_Face face;
856
857                 mapch = ch - map->start;
858
859                 if (developer.integer)
860                         Con_Print("glyphinfo: ------------- GLYPH INFO -----------------\n");
861
862                 ++gC;
863                 if (gC >= FONT_CHARS_PER_LINE)
864                 {
865                         gC -= FONT_CHARS_PER_LINE;
866                         ++gR;
867                 }
868
869                 imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
870                 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
871                 // we need the glyphIndex
872                 face = font->face;
873                 usefont = NULL;
874                 if (font->image_font && mapch == ch && img_fontmap[mapch])
875                 {
876                         map->glyphs[mapch].image = true;
877                         continue;
878                 }
879                 glyphIndex = qFT_Get_Char_Index(face, ch);
880                 if (glyphIndex == 0)
881                 {
882                         // by convention, 0 is the "missing-glyph"-glyph
883                         // try to load from a fallback font
884                         for(usefont = font->next; usefont != NULL; usefont = usefont->next)
885                         {
886                                 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
887                                         continue;
888                                 // try that glyph
889                                 face = usefont->face;
890                                 glyphIndex = qFT_Get_Char_Index(face, ch);
891                                 if (glyphIndex == 0)
892                                         continue;
893                                 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER);
894                                 if (status)
895                                         continue;
896                                 break;
897                         }
898                         if (!usefont)
899                         {
900                                 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
901                                 // now we let it use the "missing-glyph"-glyph
902                                 face = font->face;
903                                 glyphIndex = 0;
904                         }
905                 }
906
907                 if (!usefont)
908                 {
909                         usefont = font;
910                         face = font->face;
911                         status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER);
912                         if (status)
913                         {
914                                 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
915                                 Con_Printf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
916                                 continue;
917                         }
918                 }
919
920                 glyph = face->glyph;
921                 bmp = &glyph->bitmap;
922
923                 w = bmp->width;
924                 h = bmp->rows;
925
926                 if (w > map->glyphSize || h > map->glyphSize) {
927                         Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
928                         if (w > map->glyphSize)
929                                 w = map->glyphSize;
930                         if (h > map->glyphSize)
931                                 h = map->glyphSize;
932                 }
933
934                 switch (bmp->pixel_mode)
935                 {
936                 case FT_PIXEL_MODE_MONO:
937                         if (developer.integer)
938                                 Con_Print("glyphinfo:   Pixel Mode: MONO\n");
939                         break;
940                 case FT_PIXEL_MODE_GRAY2:
941                         if (developer.integer)
942                                 Con_Print("glyphinfo:   Pixel Mode: GRAY2\n");
943                         break;
944                 case FT_PIXEL_MODE_GRAY4:
945                         if (developer.integer)
946                                 Con_Print("glyphinfo:   Pixel Mode: GRAY4\n");
947                         break;
948                 case FT_PIXEL_MODE_GRAY:
949                         if (developer.integer)
950                                 Con_Print("glyphinfo:   Pixel Mode: GRAY\n");
951                         break;
952                 default:
953                         if (developer.integer)
954                                 Con_Printf("glyphinfo:   Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
955                         Mem_Free(data);
956                         Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
957                         return false;
958                 }
959                 for (y = 0; y < h; ++y)
960                 {
961                         dst = imagedata + y * pitch;
962                         src = bmp->buffer + y * bmp->pitch;
963
964                         switch (bmp->pixel_mode)
965                         {
966                         case FT_PIXEL_MODE_MONO:
967                                 dst += bytesPerPixel - 1; // shift to alpha byte
968                                 for (x = 0; x < bmp->width; x += 8)
969                                 {
970                                         unsigned char ch = *src++;
971                                         *dst = 255 * ((ch & 0x80) >> 7); dst += bytesPerPixel;
972                                         *dst = 255 * ((ch & 0x40) >> 6); dst += bytesPerPixel;
973                                         *dst = 255 * ((ch & 0x20) >> 5); dst += bytesPerPixel;
974                                         *dst = 255 * ((ch & 0x10) >> 4); dst += bytesPerPixel;
975                                         *dst = 255 * ((ch & 0x08) >> 3); dst += bytesPerPixel;
976                                         *dst = 255 * ((ch & 0x04) >> 2); dst += bytesPerPixel;
977                                         *dst = 255 * ((ch & 0x02) >> 1); dst += bytesPerPixel;
978                                         *dst = 255 * ((ch & 0x01) >> 0); dst += bytesPerPixel;
979                                 }
980                                 break;
981                         case FT_PIXEL_MODE_GRAY2:
982                                 dst += bytesPerPixel - 1; // shift to alpha byte
983                                 for (x = 0; x < bmp->width; x += 4)
984                                 {
985                                         unsigned char ch = *src++;
986                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
987                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
988                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
989                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
990                                 }
991                                 break;
992                         case FT_PIXEL_MODE_GRAY4:
993                                 dst += bytesPerPixel - 1; // shift to alpha byte
994                                 for (x = 0; x < bmp->width; x += 2)
995                                 {
996                                         unsigned char ch = *src++;
997                                         *dst = ( ((ch & 0xF0) >> 4) * 0x24); dst += bytesPerPixel;
998                                         *dst = ( ((ch & 0x0F) ) * 0x24); dst += bytesPerPixel;
999                                 }
1000                                 break;
1001                         case FT_PIXEL_MODE_GRAY:
1002                                 // in this case pitch should equal width
1003                                 for (tp = 0; tp < bmp->pitch; ++tp)
1004                                         dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
1005
1006                                 //memcpy((void*)dst, (void*)src, bmp->pitch);
1007                                 //dst += bmp->pitch;
1008                                 break;
1009                         default:
1010                                 break;
1011                         }
1012                 }
1013
1014                 // now fill map->glyphs[ch - map->start]
1015                 mapglyph = &map->glyphs[mapch];
1016
1017                 {
1018                         // old way
1019                         // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1020
1021                         double bearingX = (glyph->metrics.horiBearingX >> 6) / map->size;
1022                         double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1023                         double advance = (glyph->advance.x >> 6) / map->size;
1024                         double mWidth = (glyph->metrics.width >> 6) / map->size;
1025                         double mHeight = (glyph->metrics.height >> 6) / map->size;
1026
1027                         mapglyph->vxmin = bearingX;
1028                         mapglyph->vxmax = bearingX + mWidth;
1029                         mapglyph->vymin = -bearingY;
1030                         mapglyph->vymax = mHeight - bearingY;
1031                         mapglyph->txmin = ( (double)(gC * map->glyphSize) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1032                         mapglyph->txmax = mapglyph->txmin + (double)bmp->width / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1033                         mapglyph->tymin = ( (double)(gR * map->glyphSize) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1034                         mapglyph->tymax = mapglyph->tymin + (double)bmp->rows / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1035                         //mapglyph->advance_x = advance * usefont->size;
1036                         mapglyph->advance_x = advance;
1037                         mapglyph->advance_y = 0;
1038
1039                         if (developer.integer)
1040                         {
1041                                 Con_Printf("glyphinfo:   Glyph: %lu   at (%i, %i)\n", (unsigned long)ch, gC, gR);
1042                                 Con_Printf("glyphinfo:   %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1043                                 if (ch >= 32 && ch <= 128)
1044                                         Con_Printf("glyphinfo:   Character: %c\n", (int)ch);
1045                                 Con_Printf("glyphinfo:   Vertex info:\n");
1046                                 Con_Printf("glyphinfo:     X: ( %f  --  %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1047                                 Con_Printf("glyphinfo:     Y: ( %f  --  %f )\n", mapglyph->vymin, mapglyph->vymax);
1048                                 Con_Printf("glyphinfo:   Texture info:\n");
1049                                 Con_Printf("glyphinfo:     S: ( %f  --  %f )\n", mapglyph->txmin, mapglyph->txmax);
1050                                 Con_Printf("glyphinfo:     T: ( %f  --  %f )\n", mapglyph->tymin, mapglyph->tymax);
1051                                 Con_Printf("glyphinfo:   Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1052                         }
1053                 }
1054                 map->glyphs[mapch].image = false;
1055         }
1056
1057         // create a texture from the data now
1058
1059         if (developer.integer)
1060         {
1061                 // view using `display -depth 8 -size 512x512 name_page.rgba` (be sure to use a correct -size parameter)
1062                 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u.rgba", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1063                 FS_WriteFile(map_identifier, data, pitch * FONT_CHAR_LINES * map->glyphSize);
1064         }
1065         dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1066
1067         // probably use bytesPerPixel here instead?
1068         if (r_font_use_alpha_textures.integer)
1069         {
1070                 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1071                                                map->glyphSize * FONT_CHARS_PER_LINE,
1072                                                map->glyphSize * FONT_CHAR_LINES,
1073                                                data, TEXTYPE_ALPHA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL);
1074         } else {
1075                 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1076                                                map->glyphSize * FONT_CHARS_PER_LINE,
1077                                                map->glyphSize * FONT_CHAR_LINES,
1078                                                data, TEXTYPE_RGBA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL);
1079         }
1080
1081         Mem_Free(data);
1082         if (!map->texture)
1083         {
1084                 // if the first try isn't successful, keep it with a broken texture
1085                 // otherwise we retry to load it every single frame where ft2 rendering is used
1086                 // this would be bad...
1087                 // only `data' must be freed
1088                 Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
1089                            font->name, mapstart->size, mapidx);
1090                 return false;
1091         }
1092         if (outmap)
1093                 *outmap = map;
1094         return true;
1095 }
1096
1097 qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
1098 {
1099         if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1100                 return false;
1101         // the first map must have been loaded already
1102         if (!font->font_maps[map_index])
1103                 return false;
1104         return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
1105 }
1106
1107 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1108 {
1109         while (start && start->start + FONT_CHARS_PER_MAP < ch)
1110                 start = start->next;
1111         if (start && start->start > ch)
1112                 return NULL;
1113         return start;
1114 }