1 /* FreeType 2 and UTF-8 encoding support for
8 #include "ft2_fontdefs.h"
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
30 ================================================================================
31 CVars introduced with the freetype extension
32 ================================================================================
35 cvar_t r_font_disable_freetype = {CVAR_SAVE, "r_font_disable_freetype", "1", "disable freetype support for fonts entirely"};
36 cvar_t r_font_use_alpha_textures = {CVAR_SAVE, "r_font_use_alpha_textures", "0", "use alpha-textures for font rendering, this should safe memory"};
37 cvar_t r_font_size_snapping = {CVAR_SAVE, "r_font_size_snapping", "1", "stick to good looking font sizes whenever possible - bad when the mod doesn't support it!"};
38 cvar_t r_font_kerning = {CVAR_SAVE, "r_font_kerning", "1", "Use kerning if available"};
39 cvar_t developer_font = {CVAR_SAVE, "developer_font", "0", "prints debug messages about fonts"};
42 ================================================================================
43 Function definitions. Taken from the freetype2 headers.
44 ================================================================================
49 (*qFT_Init_FreeType)( FT_Library *alibrary );
51 (*qFT_Done_FreeType)( FT_Library library );
54 (*qFT_New_Face)( FT_Library library,
55 const char* filepathname,
60 (*qFT_New_Memory_Face)( FT_Library library,
61 const FT_Byte* file_base,
66 (*qFT_Done_Face)( FT_Face face );
68 (*qFT_Select_Size)( FT_Face face,
69 FT_Int strike_index );
71 (*qFT_Request_Size)( FT_Face face,
72 FT_Size_Request req );
74 (*qFT_Set_Char_Size)( FT_Face face,
75 FT_F26Dot6 char_width,
76 FT_F26Dot6 char_height,
77 FT_UInt horz_resolution,
78 FT_UInt vert_resolution );
80 (*qFT_Set_Pixel_Sizes)( FT_Face face,
82 FT_UInt pixel_height );
84 (*qFT_Load_Glyph)( FT_Face face,
86 FT_Int32 load_flags );
88 (*qFT_Load_Char)( FT_Face face,
90 FT_Int32 load_flags );
92 (*qFT_Get_Char_Index)( FT_Face face,
95 (*qFT_Render_Glyph)( FT_GlyphSlot slot,
96 FT_Render_Mode render_mode );
98 (*qFT_Get_Kerning)( FT_Face face,
102 FT_Vector *akerning );
103 FT_EXPORT( FT_Error )
104 (*qFT_Attach_Stream)( FT_Face face,
105 FT_Open_Args* parameters );
107 ================================================================================
108 Support for dynamically loading the FreeType2 library
109 ================================================================================
112 static dllfunction_t ft2funcs[] =
114 {"FT_Init_FreeType", (void **) &qFT_Init_FreeType},
115 {"FT_Done_FreeType", (void **) &qFT_Done_FreeType},
116 //{"FT_New_Face", (void **) &qFT_New_Face},
117 {"FT_New_Memory_Face", (void **) &qFT_New_Memory_Face},
118 {"FT_Done_Face", (void **) &qFT_Done_Face},
119 {"FT_Select_Size", (void **) &qFT_Select_Size},
120 {"FT_Request_Size", (void **) &qFT_Request_Size},
121 {"FT_Set_Char_Size", (void **) &qFT_Set_Char_Size},
122 {"FT_Set_Pixel_Sizes", (void **) &qFT_Set_Pixel_Sizes},
123 {"FT_Load_Glyph", (void **) &qFT_Load_Glyph},
124 {"FT_Load_Char", (void **) &qFT_Load_Char},
125 {"FT_Get_Char_Index", (void **) &qFT_Get_Char_Index},
126 {"FT_Render_Glyph", (void **) &qFT_Render_Glyph},
127 {"FT_Get_Kerning", (void **) &qFT_Get_Kerning},
128 {"FT_Attach_Stream", (void **) &qFT_Attach_Stream},
132 /// Handle for FreeType2 DLL
133 static dllhandle_t ft2_dll = NULL;
135 /// Memory pool for fonts
136 static mempool_t *font_mempool= NULL;
137 static rtexturepool_t *font_texturepool = NULL;
139 /// FreeType library handle
140 static FT_Library font_ft2lib = NULL;
142 #define POSTPROCESS_MAXRADIUS 8
145 unsigned char *buf, *buf2;
146 int bufsize, bufwidth, bufheight, bufpitch;
147 float blur, outline, shadowx, shadowy, shadowz;
148 int padding_t, padding_b, padding_l, padding_r, blurpadding_lt, blurpadding_rb, outlinepadding_t, outlinepadding_b, outlinepadding_l, outlinepadding_r;
149 unsigned char circlematrix[2*POSTPROCESS_MAXRADIUS+1][2*POSTPROCESS_MAXRADIUS+1];
150 unsigned char gausstable[2*POSTPROCESS_MAXRADIUS+1];
153 static font_postprocess_t pp;
159 Unload the FreeType2 DLL
162 void Font_CloseLibrary (void)
165 Mem_FreePool(&font_mempool);
166 if (font_texturepool)
167 R_FreeTexturePool(&font_texturepool);
168 if (font_ft2lib && qFT_Done_FreeType)
170 qFT_Done_FreeType(font_ft2lib);
173 Sys_UnloadLibrary (&ft2_dll);
181 Try to load the FreeType2 DLL
184 qboolean Font_OpenLibrary (void)
186 const char* dllnames [] =
191 #elif defined(MACOSX)
192 "libfreetype.6.dylib",
201 if (r_font_disable_freetype.integer)
209 if (!Sys_LoadLibrary (dllnames, &ft2_dll, ft2funcs))
218 Initialize the freetype2 font subsystem
222 void font_start(void)
224 if (!Font_OpenLibrary())
227 if (qFT_Init_FreeType(&font_ft2lib))
229 Con_Print("ERROR: Failed to initialize the FreeType2 library!\n");
234 font_mempool = Mem_AllocPool("FONT", 0, NULL);
237 Con_Print("ERROR: Failed to allocate FONT memory pool!\n");
242 font_texturepool = R_AllocTexturePool();
243 if (!font_texturepool)
245 Con_Print("ERROR: Failed to allocate FONT texture pool!\n");
251 void font_shutdown(void)
254 for (i = 0; i < MAX_FONTS; ++i)
258 Font_UnloadFont(dp_fonts[i].ft2);
259 dp_fonts[i].ft2 = NULL;
265 void font_newmap(void)
271 Cvar_RegisterVariable(&r_font_disable_freetype);
272 Cvar_RegisterVariable(&r_font_use_alpha_textures);
273 Cvar_RegisterVariable(&r_font_size_snapping);
274 Cvar_RegisterVariable(&r_font_kerning);
275 Cvar_RegisterVariable(&developer_font);
276 // let's open it at startup already
281 ================================================================================
282 Implementation of a more or less lazy font loading and rendering code.
283 ================================================================================
286 #include "ft2_fontdefs.h"
288 ft2_font_t *Font_Alloc(void)
292 return Mem_Alloc(font_mempool, sizeof(ft2_font_t));
295 qboolean Font_Attach(ft2_font_t *font, ft2_attachment_t *attachment)
297 ft2_attachment_t *na;
299 font->attachmentcount++;
300 na = (ft2_attachment_t*)Mem_Alloc(font_mempool, sizeof(font->attachments[0]) * font->attachmentcount);
303 if (font->attachments && font->attachmentcount > 1)
305 memcpy(na, font->attachments, sizeof(font->attachments[0]) * (font->attachmentcount - 1));
306 Mem_Free(font->attachments);
308 memcpy(na + sizeof(font->attachments[0]) * (font->attachmentcount - 1), attachment, sizeof(*attachment));
309 font->attachments = na;
313 float Font_VirtualToRealSize(float sz)
319 vw = ((vid.width > 0) ? vid.width : vid_width.value);
320 vh = ((vid.height > 0) ? vid.height : vid_height.value);
321 // now try to scale to our actual size:
322 sn = sz * vh / vid_conheight.value;
324 if ( sn - (float)si >= 0.5 )
329 float Font_SnapTo(float val, float snapwidth)
331 return floor(val / snapwidth + 0.5f) * snapwidth;
334 static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font);
335 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only);
336 qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt)
339 ft2_font_t *ft2, *fbfont, *fb;
348 // check if a fallback font has been specified, if it has been, and the
349 // font fails to load, use the image font as main font
350 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
352 if (dpfnt->fallbacks[i][0])
356 if (!Font_LoadFile(name, dpfnt->req_face, &dpfnt->settings, ft2))
358 if (i >= MAX_FONT_FALLBACKS)
364 strlcpy(ft2->name, name, sizeof(ft2->name));
365 ft2->image_font = true;
366 ft2->has_kerning = false;
370 ft2->image_font = false;
373 // attempt to load fallback fonts:
375 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
377 if (!dpfnt->fallbacks[i][0])
379 if (! (fb = Font_Alloc()) )
381 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
385 if (!Font_LoadFile(dpfnt->fallbacks[i], dpfnt->fallback_faces[i], &dpfnt->settings, fb))
387 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
392 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
394 if (Font_LoadSize(fb, Font_VirtualToRealSize(dpfnt->req_sizes[s]), true))
399 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
404 // at least one size of the fallback font loaded successfully
410 if (fbfont == ft2 && ft2->image_font)
412 // no fallbacks were loaded successfully:
419 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
421 if (Font_LoadSize(ft2, Font_VirtualToRealSize(dpfnt->req_sizes[s]), false))
426 // loading failed for every requested size
427 Font_UnloadFont(ft2);
433 //Con_Printf("%i sizes loaded\n", count);
438 static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font)
441 char filename[MAX_QPATH];
445 fs_offset_t datasize;
447 memset(font, 0, sizeof(*font));
449 if (!Font_OpenLibrary())
451 if (!r_font_disable_freetype.integer)
453 Con_Printf("WARNING: can't open load font %s\n"
454 "You need the FreeType2 DLL to load font files\n",
460 font->settings = settings;
462 namelen = strlen(name);
464 memcpy(filename, name, namelen);
465 memcpy(filename + namelen, ".ttf", 5);
466 data = FS_LoadFile(filename, font_mempool, false, &datasize);
469 memcpy(filename + namelen, ".otf", 5);
470 data = FS_LoadFile(filename, font_mempool, false, &datasize);
474 ft2_attachment_t afm;
476 memcpy(filename + namelen, ".pfb", 5);
477 data = FS_LoadFile(filename, font_mempool, false, &datasize);
481 memcpy(filename + namelen, ".afm", 5);
482 afm.data = FS_LoadFile(filename, font_mempool, false, &afm.size);
485 Font_Attach(font, &afm);
491 // FS_LoadFile being not-quiet should print an error :)
494 Con_Printf("Loading font %s face %i...\n", filename, _face);
496 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
497 if (status && _face != 0)
499 Con_Printf("Failed to load face %i of %s. Falling back to face 0\n", _face, name);
501 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, 0, (FT_Face*)&font->face);
505 Con_Printf("ERROR: can't create face for %s\n"
506 "Error %i\n", // TODO: error strings
508 Font_UnloadFont(font);
512 // add the attachments
513 for (i = 0; i < font->attachmentcount; ++i)
516 memset(&args, 0, sizeof(args));
517 args.flags = FT_OPEN_MEMORY;
518 args.memory_base = (const FT_Byte*)font->attachments[i].data;
519 args.memory_size = font->attachments[i].size;
520 if (qFT_Attach_Stream(font->face, &args))
521 Con_Printf("Failed to add attachment %u to %s\n", (unsigned)i, font->name);
524 memcpy(font->name, name, namelen+1);
525 font->image_font = false;
526 font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
530 void Font_Postprocess_Update(ft2_font_t *fnt, int bpp, int w, int h)
533 float gausstable[2*POSTPROCESS_MAXRADIUS+1];
534 qboolean need_gauss = (!pp.buf || pp.blur != fnt->settings->blur || pp.shadowz != fnt->settings->shadowz);
535 qboolean need_circle = (!pp.buf || pp.outline != fnt->settings->outline || pp.shadowx != fnt->settings->shadowx || pp.shadowy != fnt->settings->shadowy);
536 pp.blur = fnt->settings->blur;
537 pp.outline = fnt->settings->outline;
538 pp.shadowx = fnt->settings->shadowx;
539 pp.shadowy = fnt->settings->shadowy;
540 pp.shadowz = fnt->settings->shadowz;
541 pp.outlinepadding_l = bound(0, ceil(pp.outline - pp.shadowx), POSTPROCESS_MAXRADIUS);
542 pp.outlinepadding_r = bound(0, ceil(pp.outline + pp.shadowx), POSTPROCESS_MAXRADIUS);
543 pp.outlinepadding_t = bound(0, ceil(pp.outline - pp.shadowy), POSTPROCESS_MAXRADIUS);
544 pp.outlinepadding_b = bound(0, ceil(pp.outline + pp.shadowy), POSTPROCESS_MAXRADIUS);
545 pp.blurpadding_lt = bound(0, ceil(pp.blur - pp.shadowz), POSTPROCESS_MAXRADIUS);
546 pp.blurpadding_rb = bound(0, ceil(pp.blur + pp.shadowz), POSTPROCESS_MAXRADIUS);
547 pp.padding_l = pp.blurpadding_lt + pp.outlinepadding_l;
548 pp.padding_r = pp.blurpadding_rb + pp.outlinepadding_r;
549 pp.padding_t = pp.blurpadding_lt + pp.outlinepadding_t;
550 pp.padding_b = pp.blurpadding_rb + pp.outlinepadding_b;
554 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
555 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));
556 for(x = -pp.blurpadding_rb; x <= pp.blurpadding_lt; ++x)
557 sum += gausstable[POSTPROCESS_MAXRADIUS+x];
558 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
559 pp.gausstable[POSTPROCESS_MAXRADIUS+x] = floor(gausstable[POSTPROCESS_MAXRADIUS+x] / sum * 255 + 0.5);
563 for(y = -POSTPROCESS_MAXRADIUS; y <= POSTPROCESS_MAXRADIUS; ++y)
564 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
566 float d = pp.outline + 1 - sqrt(pow(x + pp.shadowx, 2) + pow(y + pp.shadowy, 2));
567 pp.circlematrix[POSTPROCESS_MAXRADIUS+y][POSTPROCESS_MAXRADIUS+x] = (d >= 1) ? 255 : (d <= 0) ? 0 : floor(d * 255 + 0.5);
570 pp.bufwidth = w + pp.padding_l + pp.padding_r;
571 pp.bufheight = h + pp.padding_t + pp.padding_b;
572 pp.bufpitch = pp.bufwidth;
573 needed = pp.bufwidth * pp.bufheight;
574 if(!pp.buf || pp.bufsize < needed * 2)
578 pp.bufsize = needed * 4;
579 pp.buf = Mem_Alloc(font_mempool, pp.bufsize);
580 pp.buf2 = pp.buf + needed;
584 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)
587 Font_Postprocess_Update(fnt, bpp, w, h);
592 // perform operation, not exceeding the passed padding values,
593 // but possibly reducing them
594 *pad_l = min(*pad_l, pp.padding_l);
595 *pad_r = min(*pad_r, pp.padding_r);
596 *pad_t = min(*pad_t, pp.padding_t);
597 *pad_b = min(*pad_b, pp.padding_b);
599 // calculate gauss table
601 // outline the font (RGBA only)
602 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
604 // this is like mplayer subtitle rendering
605 // bbuffer, bitmap buffer: this is our font
606 // abuffer, alpha buffer: this is pp.buf
607 // tmp: this is pp.buf2
609 // create outline buffer
610 memset(pp.buf, 0, pp.bufwidth * pp.bufheight);
611 for(y = -*pad_t; y < h + *pad_b; ++y)
612 for(x = -*pad_l; x < w + *pad_r; ++x)
614 int x1 = max(-x, -pp.outlinepadding_r);
615 int y1 = max(-y, -pp.outlinepadding_b);
616 int x2 = min(pp.outlinepadding_l, w-1-x);
617 int y2 = min(pp.outlinepadding_t, h-1-y);
621 for(my = y1; my <= y2; ++my)
622 for(mx = x1; mx <= x2; ++mx)
624 cur = pp.circlematrix[POSTPROCESS_MAXRADIUS+my][POSTPROCESS_MAXRADIUS+mx] * (int)imagedata[(x+mx) * bpp + pitch * (y+my) + (bpp - 1)];
628 pp.buf[((x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t))] = (highest + 128) / 255;
631 // blur the outline buffer
632 if(pp.blur > 0 || pp.shadowz != 0)
635 for(y = 0; y < pp.bufheight; ++y)
636 for(x = 0; x < pp.bufwidth; ++x)
638 int x1 = max(-x, -pp.blurpadding_rb);
639 int x2 = min(pp.blurpadding_lt, pp.bufwidth-1-x);
642 for(mx = x1; mx <= x2; ++mx)
643 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+mx] * (int)pp.buf[(x+mx) + pp.bufpitch * y];
644 pp.buf2[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
648 for(y = 0; y < pp.bufheight; ++y)
649 for(x = 0; x < pp.bufwidth; ++x)
651 int y1 = max(-y, -pp.blurpadding_rb);
652 int y2 = min(pp.blurpadding_lt, pp.bufheight-1-y);
655 for(my = y1; my <= y2; ++my)
656 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+my] * (int)pp.buf2[x + pp.bufpitch * (y+my)];
657 pp.buf[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
661 // paste the outline below the font
662 for(y = -*pad_t; y < h + *pad_b; ++y)
663 for(x = -*pad_l; x < w + *pad_r; ++x)
665 unsigned char outlinealpha = pp.buf[(x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t)];
668 unsigned char oldalpha = imagedata[x * bpp + pitch * y + (bpp - 1)];
669 // a' = 1 - (1 - a1) (1 - a2)
670 unsigned char newalpha = 255 - ((255 - (int)outlinealpha) * (255 - (int)oldalpha)) / 255; // this is >= oldalpha
671 // c' = (a2 c2 - a1 a2 c1 + a1 c1) / a' = (a2 c2 + a1 (1 - a2) c1) / a'
672 unsigned char oldfactor = (255 * (int)oldalpha) / newalpha;
673 //unsigned char outlinefactor = ((255 - oldalpha) * (int)outlinealpha) / newalpha;
675 for(i = 0; i < bpp-1; ++i)
677 unsigned char c = imagedata[x * bpp + pitch * y + i];
678 c = (c * (int)oldfactor) / 255 /* + outlinecolor[i] * (int)outlinefactor */;
679 imagedata[x * bpp + pitch * y + i] = c;
681 imagedata[x * bpp + pitch * y + (bpp - 1)] = newalpha;
683 //imagedata[x * bpp + pitch * y + (bpp - 1)] |= 0x80;
689 // just calculate parameters
690 *pad_l = pp.padding_l;
691 *pad_r = pp.padding_r;
692 *pad_t = pp.padding_t;
693 *pad_b = pp.padding_b;
697 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size);
698 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
699 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only)
702 ft2_font_map_t *fmap, temp;
703 int gpad_l, gpad_r, gpad_t, gpad_b;
705 if (!(size > 0.001f && size < 1000.0f))
710 if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
713 for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
715 if (!font->font_maps[map_index])
717 // if a similar size has already been loaded, ignore this one
718 //abs(font->font_maps[map_index]->size - size) < 4
719 if (font->font_maps[map_index]->size == size)
723 if (map_index >= MAX_FONT_SIZES)
728 if (font->image_font)
729 fontface = (FT_Face)font->next->face;
731 fontface = (FT_Face)font->face;
732 return (Font_SearchSize(font, fontface, size) > 0);
735 Font_Postprocess(font, NULL, 0, 4, size*2, size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
737 memset(&temp, 0, sizeof(temp));
739 temp.glyphSize = CeilPowerOf2(size*2 + max(gpad_l + gpad_r, gpad_t + gpad_b));
740 temp.sfx = (1.0/64.0)/(double)size;
741 temp.sfy = (1.0/64.0)/(double)size;
742 temp.intSize = -1; // negative value: LoadMap must search now :)
743 if (!Font_LoadMap(font, &temp, 0, &fmap))
745 Con_Printf("ERROR: can't load the first character map for %s\n"
748 Font_UnloadFont(font);
751 font->font_maps[map_index] = temp.next;
753 fmap->sfx = temp.sfx;
754 fmap->sfy = temp.sfy;
756 // load the default kerning vector:
757 if (font->has_kerning)
761 for (l = 0; l < 256; ++l)
763 for (r = 0; r < 256; ++r)
766 ul = qFT_Get_Char_Index(font->face, l);
767 ur = qFT_Get_Char_Index(font->face, r);
768 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
770 fmap->kerning.kerning[l][r][0] = 0;
771 fmap->kerning.kerning[l][r][1] = 0;
775 fmap->kerning.kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
776 fmap->kerning.kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
784 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
789 int matchsize = -10000;
791 float fsize_x, fsize_y;
792 ft2_font_map_t **maps = font->font_maps;
794 fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value;
796 fsize_x = *outw * vid.width / vid_conwidth.value;
798 fsize_y = *outh * vid.height / vid_conheight.value;
803 fsize_x = fsize_y = 16;
813 for (m = 0; m < MAX_FONT_SIZES; ++m)
817 // "round up" to the bigger size if two equally-valued matches exist
818 nval = 0.5 * (abs(maps[m]->size - fsize_x) + abs(maps[m]->size - fsize_y));
819 if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
823 matchsize = maps[m]->size;
824 if (value == 0) // there is no better match
828 if (value <= r_font_size_snapping.value)
830 // do NOT keep the aspect for perfect rendering
831 if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
832 if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width;
837 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
839 if (index < 0 || index >= MAX_FONT_SIZES)
841 return font->font_maps[index];
844 static qboolean Font_SetSize(ft2_font_t *font, float w, float h)
846 if (font->currenth == h &&
847 ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
848 font->currentw == w)) // same size has been requested
852 // sorry, but freetype doesn't seem to care about other sizes
855 if (font->image_font)
857 if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
862 if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
870 qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
872 ft2_font_map_t *fmap;
873 if (!font->has_kerning || !r_font_kerning.integer)
875 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
877 fmap = font->font_maps[map_index];
880 if (left < 256 && right < 256)
882 //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
883 // quick-kerning, be aware of the size: scale it
884 if (outx) *outx = fmap->kerning.kerning[left][right][0];// * (w / (float)fmap->size);
885 if (outy) *outy = fmap->kerning.kerning[left][right][1];// * (h / (float)fmap->size);
893 //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
895 if (!Font_SetSize(font, w, h))
897 // this deserves an error message
898 Con_Printf("Failed to get kerning for %s\n", font->name);
901 ul = qFT_Get_Char_Index(font->face, left);
902 ur = qFT_Get_Char_Index(font->face, right);
903 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
905 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);
906 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);
910 if (!Font_SetSize(font, fmap->intSize, fmap->intSize))
912 // this deserves an error message
913 Con_Printf("Failed to get kerning for %s\n", font->name);
916 ul = qFT_Get_Char_Index(font->face, left);
917 ur = qFT_Get_Char_Index(font->face, right);
918 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
920 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);// * (w / (float)fmap->size);
921 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);// * (h / (float)fmap->size);
928 qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
930 return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
933 static void UnloadMapRec(ft2_font_map_t *map)
937 R_FreeTexture(map->texture);
941 UnloadMapRec(map->next);
945 void Font_UnloadFont(ft2_font_t *font)
948 if (font->attachments && font->attachmentcount)
950 Mem_Free(font->attachments);
951 font->attachmentcount = 0;
952 font->attachments = NULL;
954 for (i = 0; i < MAX_FONT_SIZES; ++i)
956 if (font->font_maps[i])
958 UnloadMapRec(font->font_maps[i]);
959 font->font_maps[i] = NULL;
966 qFT_Done_Face((FT_Face)font->face);
972 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size)
974 float intSize = size;
977 if (!Font_SetSize(font, intSize, intSize))
979 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, size, intSize);
982 if ((fontface->size->metrics.height>>6) <= size)
986 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, size);
993 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
995 char map_identifier[MAX_QPATH];
996 unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
1001 FT_Int32 load_flags;
1002 int gpad_l, gpad_r, gpad_t, gpad_b;
1005 int gR, gC; // glyph position: row and column
1007 ft2_font_map_t *map, *next;
1008 ft2_font_t *usefont;
1012 int bytesPerPixel = 4; // change the conversion loop too if you change this!
1017 if (r_font_use_alpha_textures.integer)
1020 if (font->image_font)
1021 fontface = (FT_Face)font->next->face;
1023 fontface = (FT_Face)font->face;
1025 switch(font->settings->antialias)
1028 switch(font->settings->hinting)
1031 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1035 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1039 load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1045 switch(font->settings->hinting)
1048 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1051 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
1054 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1058 load_flags = FT_LOAD_TARGET_NORMAL;
1064 //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
1066 if (font->image_font && mapstart->intSize < 0)
1067 mapstart->intSize = mapstart->size;
1068 if (mapstart->intSize < 0)
1071 mapstart->intSize = mapstart->size;
1074 if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1076 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
1079 if ((fontface->size->metrics.height>>6) <= mapstart->size)
1081 if (mapstart->intSize < 2)
1083 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
1086 --mapstart->intSize;
1089 if ((mapstart->intSize = Font_SearchSize(font, fontface, mapstart->size)) <= 0)
1091 Con_DPrintf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
1094 if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1096 Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
1100 map = Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
1103 Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
1107 Font_Postprocess(font, NULL, 0, bytesPerPixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
1109 // copy over the information
1110 map->size = mapstart->size;
1111 map->intSize = mapstart->intSize;
1112 map->glyphSize = mapstart->glyphSize;
1113 map->sfx = mapstart->sfx;
1114 map->sfy = mapstart->sfy;
1116 pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
1117 data = Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
1120 Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
1124 memset(map->width_of, 0, sizeof(map->width_of));
1126 // initialize as white texture with zero alpha
1128 while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
1130 if (bytesPerPixel == 4)
1140 map->start = mapidx * FONT_CHARS_PER_MAP;
1142 while(next->next && next->next->start < map->start)
1144 map->next = next->next;
1149 for (ch = map->start;
1150 ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
1153 FT_ULong glyphIndex;
1157 unsigned char *imagedata, *dst, *src;
1158 glyph_slot_t *mapglyph;
1160 int pad_l, pad_r, pad_t, pad_b;
1162 mapch = ch - map->start;
1164 if (developer_font.integer)
1165 Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
1168 if (gC >= FONT_CHARS_PER_LINE)
1170 gC -= FONT_CHARS_PER_LINE;
1174 imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
1175 imagedata += gpad_t * pitch + gpad_l * bytesPerPixel;
1176 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
1177 // we need the glyphIndex
1180 if (font->image_font && mapch == ch && img_fontmap[mapch])
1182 map->glyphs[mapch].image = true;
1185 glyphIndex = qFT_Get_Char_Index(face, ch);
1186 if (glyphIndex == 0)
1188 // by convention, 0 is the "missing-glyph"-glyph
1189 // try to load from a fallback font
1190 for(usefont = font->next; usefont != NULL; usefont = usefont->next)
1192 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
1195 face = usefont->face;
1196 glyphIndex = qFT_Get_Char_Index(face, ch);
1197 if (glyphIndex == 0)
1199 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1206 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1207 // now we let it use the "missing-glyph"-glyph
1217 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1220 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1221 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1226 glyph = face->glyph;
1227 bmp = &glyph->bitmap;
1232 if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
1233 Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
1234 if (w > map->glyphSize)
1235 w = map->glyphSize - gpad_l - gpad_r;
1236 if (h > map->glyphSize)
1240 switch (bmp->pixel_mode)
1242 case FT_PIXEL_MODE_MONO:
1243 if (developer_font.integer)
1244 Con_DPrint("glyphinfo: Pixel Mode: MONO\n");
1246 case FT_PIXEL_MODE_GRAY2:
1247 if (developer_font.integer)
1248 Con_DPrint("glyphinfo: Pixel Mode: GRAY2\n");
1250 case FT_PIXEL_MODE_GRAY4:
1251 if (developer_font.integer)
1252 Con_DPrint("glyphinfo: Pixel Mode: GRAY4\n");
1254 case FT_PIXEL_MODE_GRAY:
1255 if (developer_font.integer)
1256 Con_DPrint("glyphinfo: Pixel Mode: GRAY\n");
1259 if (developer_font.integer)
1260 Con_DPrintf("glyphinfo: Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1262 Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1265 for (y = 0; y < h; ++y)
1267 dst = imagedata + y * pitch;
1268 src = bmp->buffer + y * bmp->pitch;
1270 switch (bmp->pixel_mode)
1272 case FT_PIXEL_MODE_MONO:
1273 dst += bytesPerPixel - 1; // shift to alpha byte
1274 for (x = 0; x < bmp->width; x += 8)
1276 unsigned char ch = *src++;
1277 *dst = 255 * !!((ch & 0x80) >> 7); dst += bytesPerPixel;
1278 *dst = 255 * !!((ch & 0x40) >> 6); dst += bytesPerPixel;
1279 *dst = 255 * !!((ch & 0x20) >> 5); dst += bytesPerPixel;
1280 *dst = 255 * !!((ch & 0x10) >> 4); dst += bytesPerPixel;
1281 *dst = 255 * !!((ch & 0x08) >> 3); dst += bytesPerPixel;
1282 *dst = 255 * !!((ch & 0x04) >> 2); dst += bytesPerPixel;
1283 *dst = 255 * !!((ch & 0x02) >> 1); dst += bytesPerPixel;
1284 *dst = 255 * !!((ch & 0x01) >> 0); dst += bytesPerPixel;
1287 case FT_PIXEL_MODE_GRAY2:
1288 dst += bytesPerPixel - 1; // shift to alpha byte
1289 for (x = 0; x < bmp->width; x += 4)
1291 unsigned char ch = *src++;
1292 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1293 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1294 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1295 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1298 case FT_PIXEL_MODE_GRAY4:
1299 dst += bytesPerPixel - 1; // shift to alpha byte
1300 for (x = 0; x < bmp->width; x += 2)
1302 unsigned char ch = *src++;
1303 *dst = ( ((ch & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
1304 *dst = ( ((ch & 0x0F) ) * 0x11); dst += bytesPerPixel;
1307 case FT_PIXEL_MODE_GRAY:
1308 // in this case pitch should equal width
1309 for (tp = 0; tp < bmp->pitch; ++tp)
1310 dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
1312 //memcpy((void*)dst, (void*)src, bmp->pitch);
1313 //dst += bmp->pitch;
1324 Font_Postprocess(font, imagedata, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1326 // now fill map->glyphs[ch - map->start]
1327 mapglyph = &map->glyphs[mapch];
1331 // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1333 double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1334 //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1335 double advance = (glyph->advance.x / 64.0) / map->size;
1336 //double mWidth = (glyph->metrics.width >> 6) / map->size;
1337 //double mHeight = (glyph->metrics.height >> 6) / map->size;
1339 mapglyph->txmin = ( (double)(gC * map->glyphSize) + (double)(gpad_l - pad_l) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1340 mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1341 mapglyph->tymin = ( (double)(gR * map->glyphSize) + (double)(gpad_r - pad_r) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1342 mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1343 //mapglyph->vxmin = bearingX;
1344 //mapglyph->vxmax = bearingX + mWidth;
1345 mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
1346 mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
1347 //mapglyph->vymin = -bearingY;
1348 //mapglyph->vymax = mHeight - bearingY;
1349 mapglyph->vymin = (-glyph->bitmap_top - pad_t) / map->size;
1350 mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
1351 //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);
1352 //mapglyph->advance_x = advance * usefont->size;
1353 //mapglyph->advance_x = advance;
1354 mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1355 mapglyph->advance_y = 0;
1357 if (developer_font.integer)
1359 Con_DPrintf("glyphinfo: Glyph: %lu at (%i, %i)\n", (unsigned long)ch, gC, gR);
1360 Con_DPrintf("glyphinfo: %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1361 if (ch >= 32 && ch <= 128)
1362 Con_DPrintf("glyphinfo: Character: %c\n", (int)ch);
1363 Con_DPrintf("glyphinfo: Vertex info:\n");
1364 Con_DPrintf("glyphinfo: X: ( %f -- %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1365 Con_DPrintf("glyphinfo: Y: ( %f -- %f )\n", mapglyph->vymin, mapglyph->vymax);
1366 Con_DPrintf("glyphinfo: Texture info:\n");
1367 Con_DPrintf("glyphinfo: S: ( %f -- %f )\n", mapglyph->txmin, mapglyph->txmax);
1368 Con_DPrintf("glyphinfo: T: ( %f -- %f )\n", mapglyph->tymin, mapglyph->tymax);
1369 Con_DPrintf("glyphinfo: Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1372 map->glyphs[mapch].image = false;
1375 // create a texture from the data now
1377 if (developer_font.integer > 100)
1379 // LordHavoc: why are we writing this? And why not write it as TGA using the appropriate function?
1380 // view using `display -depth 8 -size 512x512 name_page.rgba` (be sure to use a correct -size parameter)
1381 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u.rgba", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1382 FS_WriteFile(map_identifier, data, pitch * FONT_CHAR_LINES * map->glyphSize);
1384 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1386 // probably use bytesPerPixel here instead?
1387 if (r_font_use_alpha_textures.integer)
1389 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1390 map->glyphSize * FONT_CHARS_PER_LINE,
1391 map->glyphSize * FONT_CHAR_LINES,
1392 data, TEXTYPE_ALPHA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL);
1394 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1395 map->glyphSize * FONT_CHARS_PER_LINE,
1396 map->glyphSize * FONT_CHAR_LINES,
1397 data, TEXTYPE_RGBA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL);
1403 // if the first try isn't successful, keep it with a broken texture
1404 // otherwise we retry to load it every single frame where ft2 rendering is used
1405 // this would be bad...
1406 // only `data' must be freed
1407 Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
1408 font->name, mapstart->size, mapidx);
1416 qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
1418 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1420 // the first map must have been loaded already
1421 if (!font->font_maps[map_index])
1423 return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
1426 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1428 while (start && start->start + FONT_CHARS_PER_MAP <= ch)
1429 start = start->next;
1430 if (start && start->start > ch)