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 #include "image.h"
10
11 static int img_fontmap[256] = {
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,
14 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // shift+digit line
15 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // digits
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, // caps
18 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small
19 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small
20 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // specials
21 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // faces
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 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
28 };
29
30 /*
31 ================================================================================
32 CVars introduced with the freetype extension
33 ================================================================================
34 */
35
36 cvar_t r_font_disable_freetype = {CVAR_SAVE, "r_font_disable_freetype", "1", "disable freetype support for fonts entirely"};
37 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"};
38 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!"};
39 cvar_t r_font_kerning = {CVAR_SAVE, "r_font_kerning", "1", "Use kerning if available"};
40 cvar_t r_font_diskcache = {CVAR_SAVE, "r_font_diskcache", "0", "save font textures to disk for future loading rather than generating them every time"};
41 cvar_t r_font_compress = {CVAR_SAVE, "r_font_compress", "0", "use texture compression on font textures to save video memory"};
42 cvar_t r_font_nonpoweroftwo = {CVAR_SAVE, "r_font_nonpoweroftwo", "1", "use nonpoweroftwo textures for font (saves memory, potentially slower)"};
43 cvar_t developer_font = {CVAR_SAVE, "developer_font", "0", "prints debug messages about fonts"};
44
45 #ifndef DP_FREETYPE_STATIC
46
47 /*
48 ================================================================================
49 Function definitions. Taken from the freetype2 headers.
50 ================================================================================
51 */
52
53
54 FT_EXPORT( FT_Error )
55 (*qFT_Init_FreeType)( FT_Library *alibrary );
56 FT_EXPORT( FT_Error )
57 (*qFT_Done_FreeType)( FT_Library library );
58 /*
59 FT_EXPORT( FT_Error )
60 (*qFT_New_Face)( FT_Library library,
61 const char* filepathname,
62 FT_Long face_index,
63 FT_Face *aface );
64 */
65 FT_EXPORT( FT_Error )
66 (*qFT_New_Memory_Face)( FT_Library library,
67 const FT_Byte* file_base,
68 FT_Long file_size,
69 FT_Long face_index,
70 FT_Face *aface );
71 FT_EXPORT( FT_Error )
72 (*qFT_Done_Face)( FT_Face face );
73 FT_EXPORT( FT_Error )
74 (*qFT_Select_Size)( FT_Face face,
75 FT_Int strike_index );
76 FT_EXPORT( FT_Error )
77 (*qFT_Request_Size)( FT_Face face,
78 FT_Size_Request req );
79 FT_EXPORT( FT_Error )
80 (*qFT_Set_Char_Size)( FT_Face face,
81 FT_F26Dot6 char_width,
82 FT_F26Dot6 char_height,
83 FT_UInt horz_resolution,
84 FT_UInt vert_resolution );
85 FT_EXPORT( FT_Error )
86 (*qFT_Set_Pixel_Sizes)( FT_Face face,
87 FT_UInt pixel_width,
88 FT_UInt pixel_height );
89 FT_EXPORT( FT_Error )
90 (*qFT_Load_Glyph)( FT_Face face,
91 FT_UInt glyph_index,
92 FT_Int32 load_flags );
93 FT_EXPORT( FT_Error )
94 (*qFT_Load_Char)( FT_Face face,
95 FT_ULong char_code,
96 FT_Int32 load_flags );
97 FT_EXPORT( FT_UInt )
98 (*qFT_Get_Char_Index)( FT_Face face,
99 FT_ULong charcode );
100 FT_EXPORT( FT_Error )
101 (*qFT_Render_Glyph)( FT_GlyphSlot slot,
102 FT_Render_Mode render_mode );
103 FT_EXPORT( FT_Error )
104 (*qFT_Get_Kerning)( FT_Face face,
105 FT_UInt left_glyph,
106 FT_UInt right_glyph,
107 FT_UInt kern_mode,
108 FT_Vector *akerning );
109 FT_EXPORT( FT_Error )
110 (*qFT_Attach_Stream)( FT_Face face,
111 FT_Open_Args* parameters );
112 /*
113 ================================================================================
114 Support for dynamically loading the FreeType2 library
115 ================================================================================
116 */
117
118 static dllfunction_t ft2funcs[] =
119 {
120 {"FT_Init_FreeType", (void **) &qFT_Init_FreeType},
121 {"FT_Done_FreeType", (void **) &qFT_Done_FreeType},
122 //{"FT_New_Face", (void **) &qFT_New_Face},
123 {"FT_New_Memory_Face", (void **) &qFT_New_Memory_Face},
124 {"FT_Done_Face", (void **) &qFT_Done_Face},
125 {"FT_Select_Size", (void **) &qFT_Select_Size},
126 {"FT_Request_Size", (void **) &qFT_Request_Size},
127 {"FT_Set_Char_Size", (void **) &qFT_Set_Char_Size},
128 {"FT_Set_Pixel_Sizes", (void **) &qFT_Set_Pixel_Sizes},
129 {"FT_Load_Glyph", (void **) &qFT_Load_Glyph},
130 {"FT_Load_Char", (void **) &qFT_Load_Char},
131 {"FT_Get_Char_Index", (void **) &qFT_Get_Char_Index},
132 {"FT_Render_Glyph", (void **) &qFT_Render_Glyph},
133 {"FT_Get_Kerning", (void **) &qFT_Get_Kerning},
134 {"FT_Attach_Stream", (void **) &qFT_Attach_Stream},
135 {NULL, NULL}
136 };
137
138 /// Handle for FreeType2 DLL
139 static dllhandle_t ft2_dll = NULL;
140
141 #else
142
143 FT_EXPORT( FT_Error )
144 (FT_Init_FreeType)( FT_Library *alibrary );
145 FT_EXPORT( FT_Error )
146 (FT_Done_FreeType)( FT_Library library );
147 /*
148 FT_EXPORT( FT_Error )
149 (FT_New_Face)( FT_Library library,
150 const char* filepathname,
151 FT_Long face_index,
152 FT_Face *aface );
153 */
154 FT_EXPORT( FT_Error )
155 (FT_New_Memory_Face)( FT_Library library,
156 const FT_Byte* file_base,
157 FT_Long file_size,
158 FT_Long face_index,
159 FT_Face *aface );
160 FT_EXPORT( FT_Error )
161 (FT_Done_Face)( FT_Face face );
162 FT_EXPORT( FT_Error )
163 (FT_Select_Size)( FT_Face face,
164 FT_Int strike_index );
165 FT_EXPORT( FT_Error )
166 (FT_Request_Size)( FT_Face face,
167 FT_Size_Request req );
168 FT_EXPORT( FT_Error )
169 (FT_Set_Char_Size)( FT_Face face,
170 FT_F26Dot6 char_width,
171 FT_F26Dot6 char_height,
172 FT_UInt horz_resolution,
173 FT_UInt vert_resolution );
174 FT_EXPORT( FT_Error )
175 (FT_Set_Pixel_Sizes)( FT_Face face,
176 FT_UInt pixel_width,
177 FT_UInt pixel_height );
178 FT_EXPORT( FT_Error )
179 (FT_Load_Glyph)( FT_Face face,
180 FT_UInt glyph_index,
181 FT_Int32 load_flags );
182 FT_EXPORT( FT_Error )
183 (FT_Load_Char)( FT_Face face,
184 FT_ULong char_code,
185 FT_Int32 load_flags );
186 FT_EXPORT( FT_UInt )
187 (FT_Get_Char_Index)( FT_Face face,
188 FT_ULong charcode );
189 FT_EXPORT( FT_Error )
190 (FT_Render_Glyph)( FT_GlyphSlot slot,
191 FT_Render_Mode render_mode );
192 FT_EXPORT( FT_Error )
193 (FT_Get_Kerning)( FT_Face face,
194 FT_UInt left_glyph,
195 FT_UInt right_glyph,
196 FT_UInt kern_mode,
197 FT_Vector *akerning );
198 FT_EXPORT( FT_Error )
199 (FT_Attach_Stream)( FT_Face face,
200 FT_Open_Args* parameters );
201
202 #define qFT_Init_FreeType FT_Init_FreeType
203 #define qFT_Done_FreeType FT_Done_FreeType
204 //#define qFT_New_Face FT_New_Face
205 #define qFT_New_Memory_Face FT_New_Memory_Face
206 #define qFT_Done_Face FT_Done_Face
207 #define qFT_Select_Size FT_Select_Size
208 #define qFT_Request_Size FT_Request_Size
209 #define qFT_Set_Char_Size FT_Set_Char_Size
210 #define qFT_Set_Pixel_Sizes FT_Set_Pixel_Sizes
211 #define qFT_Load_Glyph FT_Load_Glyph
212 #define qFT_Load_Char FT_Load_Char
213 #define qFT_Get_Char_Index FT_Get_Char_Index
214 #define qFT_Render_Glyph FT_Render_Glyph
215 #define qFT_Get_Kerning FT_Get_Kerning
216 #define qFT_Attach_Stream FT_Attach_Stream
217
218 #endif
219
220 /// Memory pool for fonts
221 static mempool_t *font_mempool= NULL;
222
223 /// FreeType library handle
224 static FT_Library font_ft2lib = NULL;
225
226 #define POSTPROCESS_MAXRADIUS 8
227 typedef struct
228 {
229 unsigned char *buf, *buf2;
230 int bufsize, bufwidth, bufheight, bufpitch;
231 float blur, outline, shadowx, shadowy, shadowz;
232 int padding_t, padding_b, padding_l, padding_r, blurpadding_lt, blurpadding_rb, outlinepadding_t, outlinepadding_b, outlinepadding_l, outlinepadding_r;
233 unsigned char circlematrix[2*POSTPROCESS_MAXRADIUS+1][2*POSTPROCESS_MAXRADIUS+1];
234 unsigned char gausstable[2*POSTPROCESS_MAXRADIUS+1];
235 }
236 font_postprocess_t;
237 static font_postprocess_t pp;
238
239 typedef struct fontfilecache_s
240 {
241 unsigned char *buf;
242 fs_offset_t len;
243 int refcount;
244 char path[MAX_QPATH];
245 }
246 fontfilecache_t;
247 #define MAX_FONTFILES 8
248 static fontfilecache_t fontfiles[MAX_FONTFILES];
fontfilecache_LoadFile(const char * path,qboolean quiet,fs_offset_t * filesizepointer)249 static const unsigned char *fontfilecache_LoadFile(const char *path, qboolean quiet, fs_offset_t *filesizepointer)
250 {
251 int i;
252 unsigned char *buf;
253
254 for(i = 0; i < MAX_FONTFILES; ++i)
255 {
256 if(fontfiles[i].refcount > 0)
257 if(!strcmp(path, fontfiles[i].path))
258 {
259 *filesizepointer = fontfiles[i].len;
260 ++fontfiles[i].refcount;
261 return fontfiles[i].buf;
262 }
263 }
264
265 buf = FS_LoadFile(path, font_mempool, quiet, filesizepointer);
266 if(buf)
267 {
268 for(i = 0; i < MAX_FONTFILES; ++i)
269 if(fontfiles[i].refcount <= 0)
270 {
271 strlcpy(fontfiles[i].path, path, sizeof(fontfiles[i].path));
272 fontfiles[i].len = *filesizepointer;
273 fontfiles[i].buf = buf;
274 fontfiles[i].refcount = 1;
275 return buf;
276 }
277 }
278
279 return buf;
280 }
fontfilecache_Free(const unsigned char * buf)281 static void fontfilecache_Free(const unsigned char *buf)
282 {
283 int i;
284 for(i = 0; i < MAX_FONTFILES; ++i)
285 {
286 if(fontfiles[i].refcount > 0)
287 if(fontfiles[i].buf == buf)
288 {
289 if(--fontfiles[i].refcount <= 0)
290 {
291 Mem_Free(fontfiles[i].buf);
292 fontfiles[i].buf = NULL;
293 }
294 return;
295 }
296 }
297 // if we get here, it used regular allocation
298 Mem_Free((void *) buf);
299 }
fontfilecache_FreeAll(void)300 static void fontfilecache_FreeAll(void)
301 {
302 int i;
303 for(i = 0; i < MAX_FONTFILES; ++i)
304 {
305 if(fontfiles[i].refcount > 0)
306 Mem_Free(fontfiles[i].buf);
307 fontfiles[i].buf = NULL;
308 fontfiles[i].refcount = 0;
309 }
310 }
311
312 /*
313 ====================
314 Font_CloseLibrary
315
316 Unload the FreeType2 DLL
317 ====================
318 */
Font_CloseLibrary(void)319 void Font_CloseLibrary (void)
320 {
321 fontfilecache_FreeAll();
322 if (font_mempool)
323 Mem_FreePool(&font_mempool);
324 if (font_ft2lib && qFT_Done_FreeType)
325 {
326 qFT_Done_FreeType(font_ft2lib);
327 font_ft2lib = NULL;
328 }
329 #ifndef DP_FREETYPE_STATIC
330 Sys_UnloadLibrary (&ft2_dll);
331 #endif
332 pp.buf = NULL;
333 }
334
335 /*
336 ====================
337 Font_OpenLibrary
338
339 Try to load the FreeType2 DLL
340 ====================
341 */
Font_OpenLibrary(void)342 qboolean Font_OpenLibrary (void)
343 {
344 #ifndef DP_FREETYPE_STATIC
345 const char* dllnames [] =
346 {
347 #if defined(WIN32)
348 "libfreetype-6.dll",
349 "freetype6.dll",
350 #elif defined(MACOSX)
351 "libfreetype.6.dylib",
352 "libfreetype.dylib",
353 #else
354 "libfreetype.so.6",
355 "libfreetype.so",
356 #endif
357 NULL
358 };
359 #endif
360
361 if (r_font_disable_freetype.integer)
362 return false;
363
364 #ifndef DP_FREETYPE_STATIC
365 // Already loaded?
366 if (ft2_dll)
367 return true;
368
369 // Load the DLL
370 if (!Sys_LoadLibrary (dllnames, &ft2_dll, ft2funcs))
371 return false;
372 #endif
373 return true;
374 }
375
376 /*
377 ====================
378 Font_Init
379
380 Initialize the freetype2 font subsystem
381 ====================
382 */
383
font_start(void)384 void font_start(void)
385 {
386 if (!Font_OpenLibrary())
387 return;
388
389 if (qFT_Init_FreeType(&font_ft2lib))
390 {
391 Con_Print("ERROR: Failed to initialize the FreeType2 library!\n");
392 Font_CloseLibrary();
393 return;
394 }
395
396 font_mempool = Mem_AllocPool("FONT", 0, NULL);
397 if (!font_mempool)
398 {
399 Con_Print("ERROR: Failed to allocate FONT memory pool!\n");
400 Font_CloseLibrary();
401 return;
402 }
403 }
404
font_shutdown(void)405 void font_shutdown(void)
406 {
407 int i;
408 for (i = 0; i < dp_fonts.maxsize; ++i)
409 {
410 if (dp_fonts.f[i].ft2)
411 {
412 Font_UnloadFont(dp_fonts.f[i].ft2);
413 dp_fonts.f[i].ft2 = NULL;
414 }
415 }
416 Font_CloseLibrary();
417 }
418
font_newmap(void)419 void font_newmap(void)
420 {
421 }
422
Font_Init(void)423 void Font_Init(void)
424 {
425 Cvar_RegisterVariable(&r_font_nonpoweroftwo);
426 Cvar_RegisterVariable(&r_font_disable_freetype);
427 Cvar_RegisterVariable(&r_font_use_alpha_textures);
428 Cvar_RegisterVariable(&r_font_size_snapping);
429 Cvar_RegisterVariable(&r_font_kerning);
430 Cvar_RegisterVariable(&r_font_diskcache);
431 Cvar_RegisterVariable(&r_font_compress);
432 Cvar_RegisterVariable(&developer_font);
433
434 // let's open it at startup already
435 Font_OpenLibrary();
436 }
437
438 /*
439 ================================================================================
440 Implementation of a more or less lazy font loading and rendering code.
441 ================================================================================
442 */
443
444 #include "ft2_fontdefs.h"
445
Font_Alloc(void)446 ft2_font_t *Font_Alloc(void)
447 {
448 #ifndef DP_FREETYPE_STATIC
449 if (!ft2_dll)
450 #else
451 if (r_font_disable_freetype.integer)
452 #endif
453 return NULL;
454 return (ft2_font_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_t));
455 }
456
Font_Attach(ft2_font_t * font,ft2_attachment_t * attachment)457 static qboolean Font_Attach(ft2_font_t *font, ft2_attachment_t *attachment)
458 {
459 ft2_attachment_t *na;
460
461 font->attachmentcount++;
462 na = (ft2_attachment_t*)Mem_Alloc(font_mempool, sizeof(font->attachments[0]) * font->attachmentcount);
463 if (na == NULL)
464 return false;
465 if (font->attachments && font->attachmentcount > 1)
466 {
467 memcpy(na, font->attachments, sizeof(font->attachments[0]) * (font->attachmentcount - 1));
468 Mem_Free(font->attachments);
469 }
470 memcpy(na + sizeof(font->attachments[0]) * (font->attachmentcount - 1), attachment, sizeof(*attachment));
471 font->attachments = na;
472 return true;
473 }
474
Font_VirtualToRealSize(float sz)475 float Font_VirtualToRealSize(float sz)
476 {
477 int vh;
478 //int vw;
479 int si;
480 float sn;
481 if(sz < 0)
482 return sz;
483 //vw = ((vid.width > 0) ? vid.width : vid_width.value);
484 vh = ((vid.height > 0) ? vid.height : vid_height.value);
485 // now try to scale to our actual size:
486 sn = sz * vh / vid_conheight.value;
487 si = (int)sn;
488 if ( sn - (float)si >= 0.5 )
489 ++si;
490 return si;
491 }
492
Font_SnapTo(float val,float snapwidth)493 float Font_SnapTo(float val, float snapwidth)
494 {
495 return floor(val / snapwidth + 0.5f) * snapwidth;
496 }
497
498 static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font);
499 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only);
Font_LoadFont(const char * name,dp_font_t * dpfnt)500 qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt)
501 {
502 int s, count, i;
503 ft2_font_t *ft2, *fbfont, *fb;
504 char vabuf[1024];
505
506 ft2 = Font_Alloc();
507 if (!ft2)
508 {
509 dpfnt->ft2 = NULL;
510 return false;
511 }
512
513 // check if a fallback font has been specified, if it has been, and the
514 // font fails to load, use the image font as main font
515 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
516 {
517 if (dpfnt->fallbacks[i][0])
518 break;
519 }
520
521 if (!Font_LoadFile(name, dpfnt->req_face, &dpfnt->settings, ft2))
522 {
523 if (i >= MAX_FONT_FALLBACKS)
524 {
525 dpfnt->ft2 = NULL;
526 Mem_Free(ft2);
527 return false;
528 }
529 strlcpy(ft2->name, name, sizeof(ft2->name));
530 ft2->image_font = true;
531 ft2->has_kerning = false;
532 }
533 else
534 {
535 ft2->image_font = false;
536 }
537
538 // attempt to load fallback fonts:
539 fbfont = ft2;
540 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
541 {
542 if (!dpfnt->fallbacks[i][0])
543 break;
544 if (! (fb = Font_Alloc()) )
545 {
546 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
547 break;
548 }
549
550 if (!Font_LoadFile(dpfnt->fallbacks[i], dpfnt->fallback_faces[i], &dpfnt->settings, fb))
551 {
552 if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.tga", dpfnt->fallbacks[i])))
553 if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.png", dpfnt->fallbacks[i])))
554 if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.jpg", dpfnt->fallbacks[i])))
555 if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.pcx", dpfnt->fallbacks[i])))
556 Con_Printf("Failed to load font %s for fallback %i of font %s\n", dpfnt->fallbacks[i], i, name);
557 Mem_Free(fb);
558 continue;
559 }
560 count = 0;
561 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
562 {
563 if (Font_LoadSize(fb, Font_VirtualToRealSize(dpfnt->req_sizes[s]), true))
564 ++count;
565 }
566 if (!count)
567 {
568 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
569 Font_UnloadFont(fb);
570 Mem_Free(fb);
571 break;
572 }
573 // at least one size of the fallback font loaded successfully
574 // link it:
575 fbfont->next = fb;
576 fbfont = fb;
577 }
578
579 if (fbfont == ft2 && ft2->image_font)
580 {
581 // no fallbacks were loaded successfully:
582 dpfnt->ft2 = NULL;
583 Mem_Free(ft2);
584 return false;
585 }
586
587 count = 0;
588 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
589 {
590 if (Font_LoadSize(ft2, Font_VirtualToRealSize(dpfnt->req_sizes[s]), false))
591 ++count;
592 }
593 if (!count)
594 {
595 // loading failed for every requested size
596 Font_UnloadFont(ft2);
597 Mem_Free(ft2);
598 dpfnt->ft2 = NULL;
599 return false;
600 }
601
602 //Con_Printf("%i sizes loaded\n", count);
603 dpfnt->ft2 = ft2;
604 return true;
605 }
606
Font_LoadFile(const char * name,int _face,ft2_settings_t * settings,ft2_font_t * font)607 static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font)
608 {
609 size_t namelen;
610 char filename[MAX_QPATH];
611 int status;
612 size_t i;
613 const unsigned char *data;
614 fs_offset_t datasize;
615
616 memset(font, 0, sizeof(*font));
617
618 if (!Font_OpenLibrary())
619 {
620 if (!r_font_disable_freetype.integer)
621 {
622 Con_Printf("WARNING: can't open load font %s\n"
623 "You need the FreeType2 DLL to load font files\n",
624 name);
625 }
626 return false;
627 }
628
629 font->settings = settings;
630
631 namelen = strlen(name);
632 if (namelen + 5 > sizeof(filename))
633 {
634 Con_Printf("WARNING: too long font name. Cannot load this.\n");
635 return false;
636 }
637
638 // try load direct file
639 memcpy(filename, name, namelen+1);
640 data = fontfilecache_LoadFile(filename, false, &datasize);
641 // try load .ttf
642 if (!data)
643 {
644 memcpy(filename + namelen, ".ttf", 5);
645 data = fontfilecache_LoadFile(filename, false, &datasize);
646 }
647 // try load .otf
648 if (!data)
649 {
650 memcpy(filename + namelen, ".otf", 5);
651 data = fontfilecache_LoadFile(filename, false, &datasize);
652 }
653 // try load .pfb/afm
654 if (!data)
655 {
656 ft2_attachment_t afm;
657
658 memcpy(filename + namelen, ".pfb", 5);
659 data = fontfilecache_LoadFile(filename, false, &datasize);
660
661 if (data)
662 {
663 memcpy(filename + namelen, ".afm", 5);
664 afm.data = fontfilecache_LoadFile(filename, false, &afm.size);
665
666 if (afm.data)
667 Font_Attach(font, &afm);
668 }
669 }
670 if (!data)
671 {
672 // FS_LoadFile being not-quiet should print an error :)
673 return false;
674 }
675 Con_DPrintf("Loading font %s face %i...\n", filename, _face);
676
677 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
678 if (status && _face != 0)
679 {
680 Con_Printf("Failed to load face %i of %s. Falling back to face 0\n", _face, name);
681 _face = 0;
682 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
683 }
684 font->data = data;
685 if (status)
686 {
687 Con_Printf("ERROR: can't create face for %s\n"
688 "Error %i\n", // TODO: error strings
689 name, status);
690 Font_UnloadFont(font);
691 return false;
692 }
693
694 // add the attachments
695 for (i = 0; i < font->attachmentcount; ++i)
696 {
697 FT_Open_Args args;
698 memset(&args, 0, sizeof(args));
699 args.flags = FT_OPEN_MEMORY;
700 args.memory_base = (const FT_Byte*)font->attachments[i].data;
701 args.memory_size = font->attachments[i].size;
702 if (qFT_Attach_Stream((FT_Face)font->face, &args))
703 Con_Printf("Failed to add attachment %u to %s\n", (unsigned)i, font->name);
704 }
705
706 strlcpy(font->name, name, sizeof(font->name));
707 font->image_font = false;
708 font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
709 return true;
710 }
711
Font_Postprocess_Update(ft2_font_t * fnt,int bpp,int w,int h)712 static void Font_Postprocess_Update(ft2_font_t *fnt, int bpp, int w, int h)
713 {
714 int needed, x, y;
715 float gausstable[2*POSTPROCESS_MAXRADIUS+1];
716 qboolean need_gauss = (!pp.buf || pp.blur != fnt->settings->blur || pp.shadowz != fnt->settings->shadowz);
717 qboolean need_circle = (!pp.buf || pp.outline != fnt->settings->outline || pp.shadowx != fnt->settings->shadowx || pp.shadowy != fnt->settings->shadowy);
718 pp.blur = fnt->settings->blur;
719 pp.outline = fnt->settings->outline;
720 pp.shadowx = fnt->settings->shadowx;
721 pp.shadowy = fnt->settings->shadowy;
722 pp.shadowz = fnt->settings->shadowz;
723 pp.outlinepadding_l = bound(0, ceil(pp.outline - pp.shadowx), POSTPROCESS_MAXRADIUS);
724 pp.outlinepadding_r = bound(0, ceil(pp.outline + pp.shadowx), POSTPROCESS_MAXRADIUS);
725 pp.outlinepadding_t = bound(0, ceil(pp.outline - pp.shadowy), POSTPROCESS_MAXRADIUS);
726 pp.outlinepadding_b = bound(0, ceil(pp.outline + pp.shadowy), POSTPROCESS_MAXRADIUS);
727 pp.blurpadding_lt = bound(0, ceil(pp.blur - pp.shadowz), POSTPROCESS_MAXRADIUS);
728 pp.blurpadding_rb = bound(0, ceil(pp.blur + pp.shadowz), POSTPROCESS_MAXRADIUS);
729 pp.padding_l = pp.blurpadding_lt + pp.outlinepadding_l;
730 pp.padding_r = pp.blurpadding_rb + pp.outlinepadding_r;
731 pp.padding_t = pp.blurpadding_lt + pp.outlinepadding_t;
732 pp.padding_b = pp.blurpadding_rb + pp.outlinepadding_b;
733 if(need_gauss)
734 {
735 float sum = 0;
736 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
737 gausstable[POSTPROCESS_MAXRADIUS+x] = (pp.blur > 0 ? exp(-(pow(x + pp.shadowz, 2))/(pp.blur*pp.blur * 2)) : (floor(x + pp.shadowz + 0.5) == 0));
738 for(x = -pp.blurpadding_rb; x <= pp.blurpadding_lt; ++x)
739 sum += gausstable[POSTPROCESS_MAXRADIUS+x];
740 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
741 pp.gausstable[POSTPROCESS_MAXRADIUS+x] = floor(gausstable[POSTPROCESS_MAXRADIUS+x] / sum * 255 + 0.5);
742 }
743 if(need_circle)
744 {
745 for(y = -POSTPROCESS_MAXRADIUS; y <= POSTPROCESS_MAXRADIUS; ++y)
746 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
747 {
748 float d = pp.outline + 1 - sqrt(pow(x + pp.shadowx, 2) + pow(y + pp.shadowy, 2));
749 pp.circlematrix[POSTPROCESS_MAXRADIUS+y][POSTPROCESS_MAXRADIUS+x] = (d >= 1) ? 255 : (d <= 0) ? 0 : floor(d * 255 + 0.5);
750 }
751 }
752 pp.bufwidth = w + pp.padding_l + pp.padding_r;
753 pp.bufheight = h + pp.padding_t + pp.padding_b;
754 pp.bufpitch = pp.bufwidth;
755 needed = pp.bufwidth * pp.bufheight;
756 if(!pp.buf || pp.bufsize < needed * 2)
757 {
758 if(pp.buf)
759 Mem_Free(pp.buf);
760 pp.bufsize = needed * 4;
761 pp.buf = (unsigned char *)Mem_Alloc(font_mempool, pp.bufsize);
762 pp.buf2 = pp.buf + needed;
763 }
764 }
765
Font_Postprocess(ft2_font_t * fnt,unsigned char * imagedata,int pitch,int bpp,int w,int h,int * pad_l,int * pad_r,int * pad_t,int * pad_b)766 static void Font_Postprocess(ft2_font_t *fnt, unsigned char *imagedata, int pitch, int bpp, int w, int h, int *pad_l, int *pad_r, int *pad_t, int *pad_b)
767 {
768 int x, y;
769
770 // calculate gauss table
771 Font_Postprocess_Update(fnt, bpp, w, h);
772
773 if(imagedata)
774 {
775 // enlarge buffer
776 // perform operation, not exceeding the passed padding values,
777 // but possibly reducing them
778 *pad_l = min(*pad_l, pp.padding_l);
779 *pad_r = min(*pad_r, pp.padding_r);
780 *pad_t = min(*pad_t, pp.padding_t);
781 *pad_b = min(*pad_b, pp.padding_b);
782
783 // outline the font (RGBA only)
784 if(bpp == 4 && (pp.outline > 0 || pp.blur > 0 || pp.shadowx != 0 || pp.shadowy != 0 || pp.shadowz != 0)) // we can only do this in BGRA
785 {
786 // this is like mplayer subtitle rendering
787 // bbuffer, bitmap buffer: this is our font
788 // abuffer, alpha buffer: this is pp.buf
789 // tmp: this is pp.buf2
790
791 // create outline buffer
792 memset(pp.buf, 0, pp.bufwidth * pp.bufheight);
793 for(y = -*pad_t; y < h + *pad_b; ++y)
794 for(x = -*pad_l; x < w + *pad_r; ++x)
795 {
796 int x1 = max(-x, -pp.outlinepadding_r);
797 int y1 = max(-y, -pp.outlinepadding_b);
798 int x2 = min(pp.outlinepadding_l, w-1-x);
799 int y2 = min(pp.outlinepadding_t, h-1-y);
800 int mx, my;
801 int cur = 0;
802 int highest = 0;
803 for(my = y1; my <= y2; ++my)
804 for(mx = x1; mx <= x2; ++mx)
805 {
806 cur = pp.circlematrix[POSTPROCESS_MAXRADIUS+my][POSTPROCESS_MAXRADIUS+mx] * (int)imagedata[(x+mx) * bpp + pitch * (y+my) + (bpp - 1)];
807 if(cur > highest)
808 highest = cur;
809 }
810 pp.buf[((x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t))] = (highest + 128) / 255;
811 }
812
813 // blur the outline buffer
814 if(pp.blur > 0 || pp.shadowz != 0)
815 {
816 // horizontal blur
817 for(y = 0; y < pp.bufheight; ++y)
818 for(x = 0; x < pp.bufwidth; ++x)
819 {
820 int x1 = max(-x, -pp.blurpadding_rb);
821 int x2 = min(pp.blurpadding_lt, pp.bufwidth-1-x);
822 int mx;
823 int blurred = 0;
824 for(mx = x1; mx <= x2; ++mx)
825 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+mx] * (int)pp.buf[(x+mx) + pp.bufpitch * y];
826 pp.buf2[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
827 }
828
829 // vertical blur
830 for(y = 0; y < pp.bufheight; ++y)
831 for(x = 0; x < pp.bufwidth; ++x)
832 {
833 int y1 = max(-y, -pp.blurpadding_rb);
834 int y2 = min(pp.blurpadding_lt, pp.bufheight-1-y);
835 int my;
836 int blurred = 0;
837 for(my = y1; my <= y2; ++my)
838 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+my] * (int)pp.buf2[x + pp.bufpitch * (y+my)];
839 pp.buf[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
840 }
841 }
842
843 // paste the outline below the font
844 for(y = -*pad_t; y < h + *pad_b; ++y)
845 for(x = -*pad_l; x < w + *pad_r; ++x)
846 {
847 unsigned char outlinealpha = pp.buf[(x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t)];
848 if(outlinealpha > 0)
849 {
850 unsigned char oldalpha = imagedata[x * bpp + pitch * y + (bpp - 1)];
851 // a' = 1 - (1 - a1) (1 - a2)
852 unsigned char newalpha = 255 - ((255 - (int)outlinealpha) * (255 - (int)oldalpha)) / 255; // this is >= oldalpha
853 // c' = (a2 c2 - a1 a2 c1 + a1 c1) / a' = (a2 c2 + a1 (1 - a2) c1) / a'
854 unsigned char oldfactor = (255 * (int)oldalpha) / newalpha;
855 //unsigned char outlinefactor = ((255 - oldalpha) * (int)outlinealpha) / newalpha;
856 int i;
857 for(i = 0; i < bpp-1; ++i)
858 {
859 unsigned char c = imagedata[x * bpp + pitch * y + i];
860 c = (c * (int)oldfactor) / 255 /* + outlinecolor[i] * (int)outlinefactor */;
861 imagedata[x * bpp + pitch * y + i] = c;
862 }
863 imagedata[x * bpp + pitch * y + (bpp - 1)] = newalpha;
864 }
865 //imagedata[x * bpp + pitch * y + (bpp - 1)] |= 0x80;
866 }
867 }
868 }
869 else if(pitch)
870 {
871 // perform operation, not exceeding the passed padding values,
872 // but possibly reducing them
873 *pad_l = min(*pad_l, pp.padding_l);
874 *pad_r = min(*pad_r, pp.padding_r);
875 *pad_t = min(*pad_t, pp.padding_t);
876 *pad_b = min(*pad_b, pp.padding_b);
877 }
878 else
879 {
880 // just calculate parameters
881 *pad_l = pp.padding_l;
882 *pad_r = pp.padding_r;
883 *pad_t = pp.padding_t;
884 *pad_b = pp.padding_b;
885 }
886 }
887
888 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size);
889 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
Font_LoadSize(ft2_font_t * font,float size,qboolean check_only)890 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only)
891 {
892 int map_index;
893 ft2_font_map_t *fmap, temp;
894 int gpad_l, gpad_r, gpad_t, gpad_b;
895
896 if (!(size > 0.001f && size < 1000.0f))
897 size = 0;
898
899 if (!size)
900 size = 16;
901 if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
902 return false;
903
904 for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
905 {
906 if (!font->font_maps[map_index])
907 break;
908 // if a similar size has already been loaded, ignore this one
909 //abs(font->font_maps[map_index]->size - size) < 4
910 if (font->font_maps[map_index]->size == size)
911 return true;
912 }
913
914 if (map_index >= MAX_FONT_SIZES)
915 return false;
916
917 if (check_only) {
918 FT_Face fontface;
919 if (font->image_font)
920 fontface = (FT_Face)font->next->face;
921 else
922 fontface = (FT_Face)font->face;
923 return (Font_SearchSize(font, fontface, size) > 0);
924 }
925
926 Font_Postprocess(font, NULL, 0, 4, size*2, size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
927
928 memset(&temp, 0, sizeof(temp));
929 temp.size = size;
930 temp.glyphSize = size*2 + max(gpad_l + gpad_r, gpad_t + gpad_b);
931 if (!(r_font_nonpoweroftwo.integer && vid.support.arb_texture_non_power_of_two))
932 temp.glyphSize = CeilPowerOf2(temp.glyphSize);
933 temp.sfx = (1.0/64.0)/(double)size;
934 temp.sfy = (1.0/64.0)/(double)size;
935 temp.intSize = -1; // negative value: LoadMap must search now :)
936 if (!Font_LoadMap(font, &temp, 0, &fmap))
937 {
938 Con_Printf("ERROR: can't load the first character map for %s\n"
939 "This is fatal\n",
940 font->name);
941 Font_UnloadFont(font);
942 return false;
943 }
944 font->font_maps[map_index] = temp.next;
945
946 fmap->sfx = temp.sfx;
947 fmap->sfy = temp.sfy;
948
949 // load the default kerning vector:
950 if (font->has_kerning)
951 {
952 Uchar l, r;
953 FT_Vector kernvec;
954 for (l = 0; l < 256; ++l)
955 {
956 for (r = 0; r < 256; ++r)
957 {
958 FT_ULong ul, ur;
959 ul = qFT_Get_Char_Index((FT_Face)font->face, l);
960 ur = qFT_Get_Char_Index((FT_Face)font->face, r);
961 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
962 {
963 fmap->kerning.kerning[l][r][0] = 0;
964 fmap->kerning.kerning[l][r][1] = 0;
965 }
966 else
967 {
968 fmap->kerning.kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
969 fmap->kerning.kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
970 }
971 }
972 }
973 }
974 return true;
975 }
976
Font_IndexForSize(ft2_font_t * font,float _fsize,float * outw,float * outh)977 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
978 {
979 int match = -1;
980 float value = 1000000;
981 float nval;
982 int matchsize = -10000;
983 int m;
984 float fsize_x, fsize_y;
985 ft2_font_map_t **maps = font->font_maps;
986
987 fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value;
988 if(outw && *outw)
989 fsize_x = *outw * vid.width / vid_conwidth.value;
990 if(outh && *outh)
991 fsize_y = *outh * vid.height / vid_conheight.value;
992
993 if (fsize_x < 0)
994 {
995 if(fsize_y < 0)
996 fsize_x = fsize_y = 16;
997 else
998 fsize_x = fsize_y;
999 }
1000 else
1001 {
1002 if(fsize_y < 0)
1003 fsize_y = fsize_x;
1004 }
1005
1006 for (m = 0; m < MAX_FONT_SIZES; ++m)
1007 {
1008 if (!maps[m])
1009 continue;
1010 // "round up" to the bigger size if two equally-valued matches exist
1011 nval = 0.5 * (fabs(maps[m]->size - fsize_x) + fabs(maps[m]->size - fsize_y));
1012 if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
1013 {
1014 value = nval;
1015 match = m;
1016 matchsize = maps[m]->size;
1017 if (value == 0) // there is no better match
1018 break;
1019 }
1020 }
1021 if (value <= r_font_size_snapping.value)
1022 {
1023 // do NOT keep the aspect for perfect rendering
1024 if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
1025 if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width;
1026 }
1027 return match;
1028 }
1029
Font_MapForIndex(ft2_font_t * font,int index)1030 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
1031 {
1032 if (index < 0 || index >= MAX_FONT_SIZES)
1033 return NULL;
1034 return font->font_maps[index];
1035 }
1036
Font_SetSize(ft2_font_t * font,float w,float h)1037 static qboolean Font_SetSize(ft2_font_t *font, float w, float h)
1038 {
1039 if (font->currenth == h &&
1040 ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
1041 font->currentw == w)) // same size has been requested
1042 {
1043 return true;
1044 }
1045 // sorry, but freetype doesn't seem to care about other sizes
1046 w = (int)w;
1047 h = (int)h;
1048 if (font->image_font)
1049 {
1050 if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
1051 return false;
1052 }
1053 else
1054 {
1055 if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
1056 return false;
1057 }
1058 font->currentw = w;
1059 font->currenth = h;
1060 return true;
1061 }
1062
Font_GetKerningForMap(ft2_font_t * font,int map_index,float w,float h,Uchar left,Uchar right,float * outx,float * outy)1063 qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
1064 {
1065 ft2_font_map_t *fmap;
1066 if (!font->has_kerning || !r_font_kerning.integer)
1067 return false;
1068 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1069 return false;
1070 fmap = font->font_maps[map_index];
1071 if (!fmap)
1072 return false;
1073 if (left < 256 && right < 256)
1074 {
1075 //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
1076 // quick-kerning, be aware of the size: scale it
1077 if (outx) *outx = fmap->kerning.kerning[left][right][0];// * (w / (float)fmap->size);
1078 if (outy) *outy = fmap->kerning.kerning[left][right][1];// * (h / (float)fmap->size);
1079 return true;
1080 }
1081 else
1082 {
1083 FT_Vector kernvec;
1084 FT_ULong ul, ur;
1085
1086 //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
1087 #if 0
1088 if (!Font_SetSize(font, w, h))
1089 {
1090 // this deserves an error message
1091 Con_Printf("Failed to get kerning for %s\n", font->name);
1092 return false;
1093 }
1094 ul = qFT_Get_Char_Index(font->face, left);
1095 ur = qFT_Get_Char_Index(font->face, right);
1096 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
1097 {
1098 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);
1099 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);
1100 return true;
1101 }
1102 #endif
1103 if (!Font_SetSize(font, fmap->intSize, fmap->intSize))
1104 {
1105 // this deserves an error message
1106 Con_Printf("Failed to get kerning for %s\n", font->name);
1107 return false;
1108 }
1109 ul = qFT_Get_Char_Index((FT_Face)font->face, left);
1110 ur = qFT_Get_Char_Index((FT_Face)font->face, right);
1111 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
1112 {
1113 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);// * (w / (float)fmap->size);
1114 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);// * (h / (float)fmap->size);
1115 return true;
1116 }
1117 return false;
1118 }
1119 }
1120
Font_GetKerningForSize(ft2_font_t * font,float w,float h,Uchar left,Uchar right,float * outx,float * outy)1121 qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
1122 {
1123 return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
1124 }
1125
UnloadMapRec(ft2_font_map_t * map)1126 static void UnloadMapRec(ft2_font_map_t *map)
1127 {
1128 if (map->pic)
1129 {
1130 //Draw_FreePic(map->pic); // FIXME: refcounting needed...
1131 map->pic = NULL;
1132 }
1133 if (map->next)
1134 UnloadMapRec(map->next);
1135 Mem_Free(map);
1136 }
1137
Font_UnloadFont(ft2_font_t * font)1138 void Font_UnloadFont(ft2_font_t *font)
1139 {
1140 int i;
1141
1142 // unload fallbacks
1143 if(font->next)
1144 Font_UnloadFont(font->next);
1145
1146 if (font->attachments && font->attachmentcount)
1147 {
1148 for (i = 0; i < (int)font->attachmentcount; ++i) {
1149 if (font->attachments[i].data)
1150 fontfilecache_Free(font->attachments[i].data);
1151 }
1152 Mem_Free(font->attachments);
1153 font->attachmentcount = 0;
1154 font->attachments = NULL;
1155 }
1156 for (i = 0; i < MAX_FONT_SIZES; ++i)
1157 {
1158 if (font->font_maps[i])
1159 {
1160 UnloadMapRec(font->font_maps[i]);
1161 font->font_maps[i] = NULL;
1162 }
1163 }
1164 #ifndef DP_FREETYPE_STATIC
1165 if (ft2_dll)
1166 #else
1167 if (!r_font_disable_freetype.integer)
1168 #endif
1169 {
1170 if (font->face)
1171 {
1172 qFT_Done_Face((FT_Face)font->face);
1173 font->face = NULL;
1174 }
1175 }
1176 if (font->data) {
1177 fontfilecache_Free(font->data);
1178 font->data = NULL;
1179 }
1180 }
1181
Font_SearchSize(ft2_font_t * font,FT_Face fontface,float size)1182 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size)
1183 {
1184 float intSize = size;
1185 while (1)
1186 {
1187 if (!Font_SetSize(font, intSize, intSize))
1188 {
1189 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, size, intSize);
1190 return -1;
1191 }
1192 if ((fontface->size->metrics.height>>6) <= size)
1193 return intSize;
1194 if (intSize < 2)
1195 {
1196 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, size);
1197 return -1;
1198 }
1199 --intSize;
1200 }
1201 }
1202
Font_LoadMap(ft2_font_t * font,ft2_font_map_t * mapstart,Uchar _ch,ft2_font_map_t ** outmap)1203 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
1204 {
1205 char map_identifier[MAX_QPATH];
1206 unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
1207 unsigned char *data = NULL;
1208 FT_ULong ch, mapch;
1209 int status;
1210 int tp;
1211 FT_Int32 load_flags;
1212 int gpad_l, gpad_r, gpad_t, gpad_b;
1213 char vabuf[1024];
1214
1215 int pitch;
1216 int gR, gC; // glyph position: row and column
1217
1218 ft2_font_map_t *map, *next;
1219 ft2_font_t *usefont;
1220
1221 FT_Face fontface;
1222
1223 int bytesPerPixel = 4; // change the conversion loop too if you change this!
1224
1225 if (outmap)
1226 *outmap = NULL;
1227
1228 if (r_font_use_alpha_textures.integer)
1229 bytesPerPixel = 1;
1230
1231 if (font->image_font)
1232 fontface = (FT_Face)font->next->face;
1233 else
1234 fontface = (FT_Face)font->face;
1235
1236 switch(font->settings->antialias)
1237 {
1238 case 0:
1239 switch(font->settings->hinting)
1240 {
1241 case 0:
1242 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1243 break;
1244 case 1:
1245 case 2:
1246 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1247 break;
1248 default:
1249 case 3:
1250 load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1251 break;
1252 }
1253 break;
1254 default:
1255 case 1:
1256 switch(font->settings->hinting)
1257 {
1258 case 0:
1259 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1260 break;
1261 case 1:
1262 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
1263 break;
1264 case 2:
1265 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1266 break;
1267 default:
1268 case 3:
1269 load_flags = FT_LOAD_TARGET_NORMAL;
1270 break;
1271 }
1272 break;
1273 }
1274
1275 //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
1276 //if (status)
1277 if (font->image_font && mapstart->intSize < 0)
1278 mapstart->intSize = mapstart->size;
1279 if (mapstart->intSize < 0)
1280 {
1281 /*
1282 mapstart->intSize = mapstart->size;
1283 while (1)
1284 {
1285 if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1286 {
1287 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
1288 return false;
1289 }
1290 if ((fontface->size->metrics.height>>6) <= mapstart->size)
1291 break;
1292 if (mapstart->intSize < 2)
1293 {
1294 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
1295 return false;
1296 }
1297 --mapstart->intSize;
1298 }
1299 */
1300 if ((mapstart->intSize = Font_SearchSize(font, fontface, mapstart->size)) <= 0)
1301 return false;
1302 Con_DPrintf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
1303 }
1304
1305 if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1306 {
1307 Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
1308 return false;
1309 }
1310
1311 map = (ft2_font_map_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
1312 if (!map)
1313 {
1314 Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
1315 return false;
1316 }
1317
1318 // create a totally unique name for this map, then we will use it to make a unique cachepic_t to avoid redundant textures
1319 dpsnprintf(map_identifier, sizeof(map_identifier),
1320 "%s_cache_%g_%d_%g_%g_%g_%g_%g_%u",
1321 font->name,
1322 (double) mapstart->intSize,
1323 (int) load_flags,
1324 (double) font->settings->blur,
1325 (double) font->settings->outline,
1326 (double) font->settings->shadowx,
1327 (double) font->settings->shadowy,
1328 (double) font->settings->shadowz,
1329 (unsigned) mapidx);
1330
1331 // create a cachepic_t from the data now, or reuse an existing one
1332 map->pic = Draw_CachePic_Flags(map_identifier, CACHEPICFLAG_QUIET);
1333 if (developer_font.integer)
1334 {
1335 if (!Draw_IsPicLoaded(map->pic))
1336 Con_Printf("Generating font map %s (size: %.1f MB)\n", map_identifier, mapstart->glyphSize * (256 * 4 / 1048576.0) * mapstart->glyphSize);
1337 else
1338 Con_Printf("Using cached font map %s (size: %.1f MB)\n", map_identifier, mapstart->glyphSize * (256 * 4 / 1048576.0) * mapstart->glyphSize);
1339 }
1340
1341 Font_Postprocess(font, NULL, 0, bytesPerPixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
1342
1343 // copy over the information
1344 map->size = mapstart->size;
1345 map->intSize = mapstart->intSize;
1346 map->glyphSize = mapstart->glyphSize;
1347 map->sfx = mapstart->sfx;
1348 map->sfy = mapstart->sfy;
1349
1350 pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
1351 if (!Draw_IsPicLoaded(map->pic))
1352 {
1353 data = (unsigned char *)Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
1354 if (!data)
1355 {
1356 Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
1357 Mem_Free(map);
1358 return false;
1359 }
1360 // initialize as white texture with zero alpha
1361 tp = 0;
1362 while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
1363 {
1364 if (bytesPerPixel == 4)
1365 {
1366 data[tp++] = 0xFF;
1367 data[tp++] = 0xFF;
1368 data[tp++] = 0xFF;
1369 }
1370 data[tp++] = 0x00;
1371 }
1372 }
1373
1374 memset(map->width_of, 0, sizeof(map->width_of));
1375
1376 // insert the map
1377 map->start = mapidx * FONT_CHARS_PER_MAP;
1378 next = mapstart;
1379 while(next->next && next->next->start < map->start)
1380 next = next->next;
1381 map->next = next->next;
1382 next->next = map;
1383
1384 gR = 0;
1385 gC = -1;
1386 for (ch = map->start;
1387 ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
1388 ++ch)
1389 {
1390 FT_ULong glyphIndex;
1391 int w, h, x, y;
1392 FT_GlyphSlot glyph;
1393 FT_Bitmap *bmp;
1394 unsigned char *imagedata = NULL, *dst, *src;
1395 glyph_slot_t *mapglyph;
1396 FT_Face face;
1397 int pad_l, pad_r, pad_t, pad_b;
1398
1399 mapch = ch - map->start;
1400
1401 if (developer_font.integer)
1402 Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
1403
1404 ++gC;
1405 if (gC >= FONT_CHARS_PER_LINE)
1406 {
1407 gC -= FONT_CHARS_PER_LINE;
1408 ++gR;
1409 }
1410
1411 if (data)
1412 {
1413 imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
1414 imagedata += gpad_t * pitch + gpad_l * bytesPerPixel;
1415 }
1416 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
1417 // we need the glyphIndex
1418 face = (FT_Face)font->face;
1419 usefont = NULL;
1420 if (font->image_font && mapch == ch && img_fontmap[mapch])
1421 {
1422 map->glyphs[mapch].image = true;
1423 continue;
1424 }
1425 glyphIndex = qFT_Get_Char_Index(face, ch);
1426 if (glyphIndex == 0)
1427 {
1428 // by convention, 0 is the "missing-glyph"-glyph
1429 // try to load from a fallback font
1430 for(usefont = font->next; usefont != NULL; usefont = usefont->next)
1431 {
1432 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
1433 continue;
1434 // try that glyph
1435 face = (FT_Face)usefont->face;
1436 glyphIndex = qFT_Get_Char_Index(face, ch);
1437 if (glyphIndex == 0)
1438 continue;
1439 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1440 if (status)
1441 continue;
1442 break;
1443 }
1444 if (!usefont)
1445 {
1446 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1447 // now we let it use the "missing-glyph"-glyph
1448 face = (FT_Face)font->face;
1449 glyphIndex = 0;
1450 }
1451 }
1452
1453 if (!usefont)
1454 {
1455 usefont = font;
1456 face = (FT_Face)font->face;
1457 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1458 if (status)
1459 {
1460 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1461 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1462 continue;
1463 }
1464 }
1465
1466 glyph = face->glyph;
1467 bmp = &glyph->bitmap;
1468
1469 w = bmp->width;
1470 h = bmp->rows;
1471
1472 if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
1473 Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
1474 if (w > map->glyphSize)
1475 w = map->glyphSize - gpad_l - gpad_r;
1476 if (h > map->glyphSize)
1477 h = map->glyphSize;
1478 }
1479
1480 if (imagedata)
1481 {
1482 switch (bmp->pixel_mode)
1483 {
1484 case FT_PIXEL_MODE_MONO:
1485 if (developer_font.integer)
1486 Con_DPrint("glyphinfo: Pixel Mode: MONO\n");
1487 break;
1488 case FT_PIXEL_MODE_GRAY2:
1489 if (developer_font.integer)
1490 Con_DPrint("glyphinfo: Pixel Mode: GRAY2\n");
1491 break;
1492 case FT_PIXEL_MODE_GRAY4:
1493 if (developer_font.integer)
1494 Con_DPrint("glyphinfo: Pixel Mode: GRAY4\n");
1495 break;
1496 case FT_PIXEL_MODE_GRAY:
1497 if (developer_font.integer)
1498 Con_DPrint("glyphinfo: Pixel Mode: GRAY\n");
1499 break;
1500 default:
1501 if (developer_font.integer)
1502 Con_DPrintf("glyphinfo: Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1503 Mem_Free(data);
1504 Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1505 return false;
1506 }
1507 for (y = 0; y < h; ++y)
1508 {
1509 dst = imagedata + y * pitch;
1510 src = bmp->buffer + y * bmp->pitch;
1511
1512 switch (bmp->pixel_mode)
1513 {
1514 case FT_PIXEL_MODE_MONO:
1515 dst += bytesPerPixel - 1; // shift to alpha byte
1516 for (x = 0; x < bmp->width; x += 8)
1517 {
1518 unsigned char c = *src++;
1519 *dst = 255 * !!((c & 0x80) >> 7); dst += bytesPerPixel;
1520 *dst = 255 * !!((c & 0x40) >> 6); dst += bytesPerPixel;
1521 *dst = 255 * !!((c & 0x20) >> 5); dst += bytesPerPixel;
1522 *dst = 255 * !!((c & 0x10) >> 4); dst += bytesPerPixel;
1523 *dst = 255 * !!((c & 0x08) >> 3); dst += bytesPerPixel;
1524 *dst = 255 * !!((c & 0x04) >> 2); dst += bytesPerPixel;
1525 *dst = 255 * !!((c & 0x02) >> 1); dst += bytesPerPixel;
1526 *dst = 255 * !!((c & 0x01) >> 0); dst += bytesPerPixel;
1527 }
1528 break;
1529 case FT_PIXEL_MODE_GRAY2:
1530 dst += bytesPerPixel - 1; // shift to alpha byte
1531 for (x = 0; x < bmp->width; x += 4)
1532 {
1533 unsigned char c = *src++;
1534 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytesPerPixel;
1535 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytesPerPixel;
1536 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytesPerPixel;
1537 *dst = ( ((c & 0xA0) >> 6) * 0x55 ); c <<= 2; dst += bytesPerPixel;
1538 }
1539 break;
1540 case FT_PIXEL_MODE_GRAY4:
1541 dst += bytesPerPixel - 1; // shift to alpha byte
1542 for (x = 0; x < bmp->width; x += 2)
1543 {
1544 unsigned char c = *src++;
1545 *dst = ( ((c & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
1546 *dst = ( ((c & 0x0F) ) * 0x11); dst += bytesPerPixel;
1547 }
1548 break;
1549 case FT_PIXEL_MODE_GRAY:
1550 // in this case pitch should equal width
1551 for (tp = 0; tp < bmp->pitch; ++tp)
1552 dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
1553
1554 //memcpy((void*)dst, (void*)src, bmp->pitch);
1555 //dst += bmp->pitch;
1556 break;
1557 default:
1558 break;
1559 }
1560 }
1561
1562 pad_l = gpad_l;
1563 pad_r = gpad_r;
1564 pad_t = gpad_t;
1565 pad_b = gpad_b;
1566 Font_Postprocess(font, imagedata, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1567 }
1568 else
1569 {
1570 pad_l = gpad_l;
1571 pad_r = gpad_r;
1572 pad_t = gpad_t;
1573 pad_b = gpad_b;
1574 Font_Postprocess(font, NULL, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1575 }
1576
1577
1578 // now fill map->glyphs[ch - map->start]
1579 mapglyph = &map->glyphs[mapch];
1580
1581 {
1582 // old way
1583 // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1584
1585 double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1586 //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1587 double advance = (glyph->advance.x / 64.0) / map->size;
1588 //double mWidth = (glyph->metrics.width >> 6) / map->size;
1589 //double mHeight = (glyph->metrics.height >> 6) / map->size;
1590
1591 mapglyph->txmin = ( (double)(gC * map->glyphSize) + (double)(gpad_l - pad_l) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1592 mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1593 mapglyph->tymin = ( (double)(gR * map->glyphSize) + (double)(gpad_r - pad_r) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1594 mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1595 //mapglyph->vxmin = bearingX;
1596 //mapglyph->vxmax = bearingX + mWidth;
1597 mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
1598 mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
1599 //mapglyph->vymin = -bearingY;
1600 //mapglyph->vymax = mHeight - bearingY;
1601 mapglyph->vymin = (-glyph->bitmap_top - pad_t) / map->size;
1602 mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
1603 //Con_Printf("dpi = %f %f (%f %d) %d %d\n", bmp->width / (mapglyph->vxmax - mapglyph->vxmin), bmp->rows / (mapglyph->vymax - mapglyph->vymin), map->size, map->glyphSize, (int)fontface->size->metrics.x_ppem, (int)fontface->size->metrics.y_ppem);
1604 //mapglyph->advance_x = advance * usefont->size;
1605 //mapglyph->advance_x = advance;
1606 mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1607 mapglyph->advance_y = 0;
1608
1609 if (developer_font.integer)
1610 {
1611 Con_DPrintf("glyphinfo: Glyph: %lu at (%i, %i)\n", (unsigned long)ch, gC, gR);
1612 Con_DPrintf("glyphinfo: %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1613 if (ch >= 32 && ch <= 128)
1614 Con_DPrintf("glyphinfo: Character: %c\n", (int)ch);
1615 Con_DPrintf("glyphinfo: Vertex info:\n");
1616 Con_DPrintf("glyphinfo: X: ( %f -- %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1617 Con_DPrintf("glyphinfo: Y: ( %f -- %f )\n", mapglyph->vymin, mapglyph->vymax);
1618 Con_DPrintf("glyphinfo: Texture info:\n");
1619 Con_DPrintf("glyphinfo: S: ( %f -- %f )\n", mapglyph->txmin, mapglyph->txmax);
1620 Con_DPrintf("glyphinfo: T: ( %f -- %f )\n", mapglyph->tymin, mapglyph->tymax);
1621 Con_DPrintf("glyphinfo: Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1622 }
1623 }
1624 map->glyphs[mapch].image = false;
1625 }
1626
1627 if (!Draw_IsPicLoaded(map->pic))
1628 {
1629 int w = map->glyphSize * FONT_CHARS_PER_LINE;
1630 int h = map->glyphSize * FONT_CHAR_LINES;
1631 // update the pic returned by Draw_CachePic_Flags earlier to contain our texture
1632 Draw_NewPic(map_identifier, w, h, data, r_font_use_alpha_textures.integer ? TEXTYPE_ALPHA : TEXTYPE_RGBA, TEXF_ALPHA | (r_font_compress.integer > 0 ? TEXF_COMPRESS : 0));
1633
1634 if (r_font_diskcache.integer >= 1)
1635 {
1636 // swap to BGRA for tga writing...
1637 int s = w * h;
1638 int x;
1639 int b;
1640 for (x = 0;x < s;x++)
1641 {
1642 b = data[x*4+0];
1643 data[x*4+0] = data[x*4+2];
1644 data[x*4+2] = b;
1645 }
1646 Image_WriteTGABGRA(va(vabuf, sizeof(vabuf), "%s.tga", map_identifier), w, h, data);
1647 #ifndef USE_GLES2
1648 if (r_font_compress.integer && qglGetCompressedTexImageARB && Draw_IsPicLoaded(map->pic))
1649 R_SaveTextureDDSFile(Draw_GetPicTexture(map->pic), va(vabuf, sizeof(vabuf), "dds/%s.dds", map_identifier), r_texture_dds_save.integer < 2, true);
1650 #endif
1651 }
1652 }
1653
1654 if(data)
1655 Mem_Free(data);
1656
1657 if (!Draw_IsPicLoaded(map->pic))
1658 {
1659 // if the first try isn't successful, keep it with a broken texture
1660 // otherwise we retry to load it every single frame where ft2 rendering is used
1661 // this would be bad...
1662 // only `data' must be freed
1663 Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
1664 font->name, mapstart->size, mapidx);
1665 return false;
1666 }
1667 if (outmap)
1668 *outmap = map;
1669 return true;
1670 }
1671
Font_LoadMapForIndex(ft2_font_t * font,int map_index,Uchar _ch,ft2_font_map_t ** outmap)1672 qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
1673 {
1674 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1675 return false;
1676 // the first map must have been loaded already
1677 if (!font->font_maps[map_index])
1678 return false;
1679 return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
1680 }
1681
FontMap_FindForChar(ft2_font_map_t * start,Uchar ch)1682 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1683 {
1684 while (start && start->start + FONT_CHARS_PER_MAP <= ch)
1685 start = start->next;
1686 if (start && start->start > ch)
1687 return NULL;
1688 return start;
1689 }
1690