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