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)
532 qboolean need_gauss, need_circle;
534 float gausstable[2*POSTPROCESS_MAXRADIUS+1];
535 if(!pp.buf || pp.blur != fnt->settings->blur || pp.shadowz != fnt->settings->shadowz)
537 if(!pp.buf || pp.outline != fnt->settings->outline || pp.shadowx != fnt->settings->shadowx || pp.shadowy != fnt->settings->shadowy)
539 pp.blur = fnt->settings->blur;
540 pp.outline = fnt->settings->outline;
541 pp.shadowx = fnt->settings->shadowx;
542 pp.shadowy = fnt->settings->shadowy;
543 pp.shadowz = fnt->settings->shadowz;
544 pp.outlinepadding_l = bound(0, ceil(pp.outline - pp.shadowx), POSTPROCESS_MAXRADIUS);
545 pp.outlinepadding_r = bound(0, ceil(pp.outline + pp.shadowx), POSTPROCESS_MAXRADIUS);
546 pp.outlinepadding_t = bound(0, ceil(pp.outline - pp.shadowy), POSTPROCESS_MAXRADIUS);
547 pp.outlinepadding_b = bound(0, ceil(pp.outline + pp.shadowy), POSTPROCESS_MAXRADIUS);
548 pp.blurpadding_lt = bound(0, ceil(pp.blur - pp.shadowz), POSTPROCESS_MAXRADIUS);
549 pp.blurpadding_rb = bound(0, ceil(pp.blur + pp.shadowz), POSTPROCESS_MAXRADIUS);
550 pp.padding_l = pp.blurpadding_lt + pp.outlinepadding_l;
551 pp.padding_r = pp.blurpadding_rb + pp.outlinepadding_r;
552 pp.padding_t = pp.blurpadding_lt + pp.outlinepadding_t;
553 pp.padding_b = pp.blurpadding_rb + pp.outlinepadding_b;
557 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
558 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));
559 for(x = -pp.blurpadding_rb; x <= pp.blurpadding_lt; ++x)
560 sum += gausstable[POSTPROCESS_MAXRADIUS+x];
561 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
562 pp.gausstable[POSTPROCESS_MAXRADIUS+x] = floor(gausstable[POSTPROCESS_MAXRADIUS+x] / sum * 255 + 0.5);
566 for(y = -POSTPROCESS_MAXRADIUS; y <= POSTPROCESS_MAXRADIUS; ++y)
567 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
569 float d = pp.outline + 1 - sqrt(pow(x + pp.shadowx, 2) + pow(y + pp.shadowy, 2));
570 pp.circlematrix[POSTPROCESS_MAXRADIUS+y][POSTPROCESS_MAXRADIUS+x] = (d >= 1) ? 255 : (d <= 0) ? 0 : floor(d * 255 + 0.5);
573 pp.bufwidth = w + pp.padding_l + pp.padding_r;
574 pp.bufheight = h + pp.padding_t + pp.padding_b;
575 pp.bufpitch = pp.bufwidth;
576 needed = pp.bufwidth * pp.bufheight;
577 if(!pp.buf || pp.bufsize < needed * 2)
581 pp.bufsize = needed * 4;
582 pp.buf = Mem_Alloc(font_mempool, pp.bufsize);
583 pp.buf2 = pp.buf + needed;
587 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)
590 Font_Postprocess_Update(fnt, bpp, w, h);
595 // perform operation, not exceeding the passed padding values,
596 // but possibly reducing them
597 *pad_l = min(*pad_l, pp.padding_l);
598 *pad_r = min(*pad_r, pp.padding_r);
599 *pad_t = min(*pad_t, pp.padding_t);
600 *pad_b = min(*pad_b, pp.padding_b);
602 // calculate gauss table
604 // outline the font (RGBA only)
605 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
607 // this is like mplayer subtitle rendering
608 // bbuffer, bitmap buffer: this is our font
609 // abuffer, alpha buffer: this is pp.buf
610 // tmp: this is pp.buf2
612 // create outline buffer
613 memset(pp.buf, 0, pp.bufwidth * pp.bufheight);
614 for(y = -*pad_t; y < h + *pad_b; ++y)
615 for(x = -*pad_l; x < w + *pad_r; ++x)
617 int x1 = max(-x, -pp.outlinepadding_r);
618 int y1 = max(-y, -pp.outlinepadding_b);
619 int x2 = min(pp.outlinepadding_l, w-1-x);
620 int y2 = min(pp.outlinepadding_t, h-1-y);
624 for(my = y1; my <= y2; ++my)
625 for(mx = x1; mx <= x2; ++mx)
627 cur = pp.circlematrix[POSTPROCESS_MAXRADIUS+my][POSTPROCESS_MAXRADIUS+mx] * (int)imagedata[(x+mx) * bpp + pitch * (y+my) + (bpp - 1)];
631 pp.buf[((x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t))] = (highest + 128) / 255;
634 // blur the outline buffer
635 if(pp.blur > 0 || pp.shadowz != 0)
638 for(y = 0; y < pp.bufheight; ++y)
639 for(x = 0; x < pp.bufwidth; ++x)
641 int x1 = max(-x, -pp.blurpadding_rb);
642 int x2 = min(pp.blurpadding_lt, pp.bufwidth-1-x);
645 for(mx = x1; mx <= x2; ++mx)
646 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+mx] * (int)pp.buf[(x+mx) + pp.bufpitch * y];
647 pp.buf2[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
651 for(y = 0; y < pp.bufheight; ++y)
652 for(x = 0; x < pp.bufwidth; ++x)
654 int y1 = max(-y, -pp.blurpadding_rb);
655 int y2 = min(pp.blurpadding_lt, pp.bufheight-1-y);
658 for(my = y1; my <= y2; ++my)
659 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+my] * (int)pp.buf2[x + pp.bufpitch * (y+my)];
660 pp.buf[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
664 // paste the outline below the font
665 for(y = -*pad_t; y < h + *pad_b; ++y)
666 for(x = -*pad_l; x < w + *pad_r; ++x)
668 unsigned char outlinealpha = pp.buf[(x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t)];
671 unsigned char oldalpha = imagedata[x * bpp + pitch * y + (bpp - 1)];
672 // a' = 1 - (1 - a1) (1 - a2)
673 unsigned char newalpha = 255 - ((255 - (int)outlinealpha) * (255 - (int)oldalpha)) / 255; // this is >= oldalpha
674 // c' = (a2 c2 - a1 a2 c1 + a1 c1) / a' = (a2 c2 + a1 (1 - a2) c1) / a'
675 unsigned char oldfactor = (255 * (int)oldalpha) / newalpha;
676 //unsigned char outlinefactor = ((255 - oldalpha) * (int)outlinealpha) / newalpha;
678 for(i = 0; i < bpp-1; ++i)
680 unsigned char c = imagedata[x * bpp + pitch * y + i];
681 c = (c * (int)oldfactor) / 255 /* + outlinecolor[i] * (int)outlinefactor */;
682 imagedata[x * bpp + pitch * y + i] = c;
684 imagedata[x * bpp + pitch * y + (bpp - 1)] = newalpha;
686 //imagedata[x * bpp + pitch * y + (bpp - 1)] |= 0x80;
692 // just calculate parameters
693 *pad_l = pp.padding_l;
694 *pad_r = pp.padding_r;
695 *pad_t = pp.padding_t;
696 *pad_b = pp.padding_b;
700 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size);
701 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
702 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only)
705 ft2_font_map_t *fmap, temp;
706 int gpad_l, gpad_r, gpad_t, gpad_b;
708 if (!(size > 0.001f && size < 1000.0f))
713 if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
716 for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
718 if (!font->font_maps[map_index])
720 // if a similar size has already been loaded, ignore this one
721 //abs(font->font_maps[map_index]->size - size) < 4
722 if (font->font_maps[map_index]->size == size)
726 if (map_index >= MAX_FONT_SIZES)
731 if (font->image_font)
732 fontface = (FT_Face)font->next->face;
734 fontface = (FT_Face)font->face;
735 return (Font_SearchSize(font, fontface, size) > 0);
738 Font_Postprocess(font, NULL, 0, 4, size*2, size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
740 memset(&temp, 0, sizeof(temp));
742 temp.glyphSize = CeilPowerOf2(size*2 + max(gpad_l + gpad_r, gpad_t + gpad_b));
743 temp.sfx = (1.0/64.0)/(double)size;
744 temp.sfy = (1.0/64.0)/(double)size;
745 temp.intSize = -1; // negative value: LoadMap must search now :)
746 if (!Font_LoadMap(font, &temp, 0, &fmap))
748 Con_Printf("ERROR: can't load the first character map for %s\n"
751 Font_UnloadFont(font);
754 font->font_maps[map_index] = temp.next;
756 fmap->sfx = temp.sfx;
757 fmap->sfy = temp.sfy;
759 // load the default kerning vector:
760 if (font->has_kerning)
764 for (l = 0; l < 256; ++l)
766 for (r = 0; r < 256; ++r)
769 ul = qFT_Get_Char_Index(font->face, l);
770 ur = qFT_Get_Char_Index(font->face, r);
771 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
773 fmap->kerning.kerning[l][r][0] = 0;
774 fmap->kerning.kerning[l][r][1] = 0;
778 fmap->kerning.kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
779 fmap->kerning.kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
787 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
792 int matchsize = -10000;
794 float fsize_x, fsize_y;
795 ft2_font_map_t **maps = font->font_maps;
797 fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value;
799 fsize_x = *outw * vid.width / vid_conwidth.value;
801 fsize_y = *outh * vid.height / vid_conheight.value;
806 fsize_x = fsize_y = 16;
816 for (m = 0; m < MAX_FONT_SIZES; ++m)
820 // "round up" to the bigger size if two equally-valued matches exist
821 nval = 0.5 * (abs(maps[m]->size - fsize_x) + abs(maps[m]->size - fsize_y));
822 if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
826 matchsize = maps[m]->size;
827 if (value == 0) // there is no better match
831 if (value <= r_font_size_snapping.value)
833 // do NOT keep the aspect for perfect rendering
834 if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
835 if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width;
840 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
842 if (index < 0 || index >= MAX_FONT_SIZES)
844 return font->font_maps[index];
847 static qboolean Font_SetSize(ft2_font_t *font, float w, float h)
849 if (font->currenth == h &&
850 ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
851 font->currentw == w)) // same size has been requested
855 // sorry, but freetype doesn't seem to care about other sizes
858 if (font->image_font)
860 if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
865 if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
873 qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
875 ft2_font_map_t *fmap;
876 if (!font->has_kerning || !r_font_kerning.integer)
878 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
880 fmap = font->font_maps[map_index];
883 if (left < 256 && right < 256)
885 //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
886 // quick-kerning, be aware of the size: scale it
887 if (outx) *outx = fmap->kerning.kerning[left][right][0];// * (w / (float)fmap->size);
888 if (outy) *outy = fmap->kerning.kerning[left][right][1];// * (h / (float)fmap->size);
896 //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
898 if (!Font_SetSize(font, w, h))
900 // this deserves an error message
901 Con_Printf("Failed to get kerning for %s\n", font->name);
904 ul = qFT_Get_Char_Index(font->face, left);
905 ur = qFT_Get_Char_Index(font->face, right);
906 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
908 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);
909 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);
913 if (!Font_SetSize(font, fmap->intSize, fmap->intSize))
915 // this deserves an error message
916 Con_Printf("Failed to get kerning for %s\n", font->name);
919 ul = qFT_Get_Char_Index(font->face, left);
920 ur = qFT_Get_Char_Index(font->face, right);
921 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
923 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);// * (w / (float)fmap->size);
924 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);// * (h / (float)fmap->size);
931 qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
933 return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
936 static void UnloadMapRec(ft2_font_map_t *map)
940 R_FreeTexture(map->texture);
944 UnloadMapRec(map->next);
948 void Font_UnloadFont(ft2_font_t *font)
951 if (font->attachments && font->attachmentcount)
953 Mem_Free(font->attachments);
954 font->attachmentcount = 0;
955 font->attachments = NULL;
957 for (i = 0; i < MAX_FONT_SIZES; ++i)
959 if (font->font_maps[i])
961 UnloadMapRec(font->font_maps[i]);
962 font->font_maps[i] = NULL;
969 qFT_Done_Face((FT_Face)font->face);
975 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size)
977 float intSize = size;
980 if (!Font_SetSize(font, intSize, intSize))
982 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, size, intSize);
985 if ((fontface->size->metrics.height>>6) <= size)
989 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, size);
996 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
998 char map_identifier[MAX_QPATH];
999 unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
1000 unsigned char *data;
1004 FT_Int32 load_flags;
1005 int gpad_l, gpad_r, gpad_t, gpad_b;
1008 int gR, gC; // glyph position: row and column
1010 ft2_font_map_t *map, *next;
1011 ft2_font_t *usefont;
1015 int bytesPerPixel = 4; // change the conversion loop too if you change this!
1020 if (r_font_use_alpha_textures.integer)
1023 if (font->image_font)
1024 fontface = (FT_Face)font->next->face;
1026 fontface = (FT_Face)font->face;
1028 switch(font->settings->antialias)
1031 switch(font->settings->hinting)
1034 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1038 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1042 load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1048 switch(font->settings->hinting)
1051 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1054 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
1057 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1061 load_flags = FT_LOAD_TARGET_NORMAL;
1067 //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
1069 if (font->image_font && mapstart->intSize < 0)
1070 mapstart->intSize = mapstart->size;
1071 if (mapstart->intSize < 0)
1074 mapstart->intSize = mapstart->size;
1077 if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1079 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
1082 if ((fontface->size->metrics.height>>6) <= mapstart->size)
1084 if (mapstart->intSize < 2)
1086 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
1089 --mapstart->intSize;
1092 if ((mapstart->intSize = Font_SearchSize(font, fontface, mapstart->size)) <= 0)
1094 Con_DPrintf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
1097 if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1099 Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
1103 map = Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
1106 Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
1110 Font_Postprocess(font, NULL, 0, bytesPerPixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
1112 // copy over the information
1113 map->size = mapstart->size;
1114 map->intSize = mapstart->intSize;
1115 map->glyphSize = mapstart->glyphSize;
1116 map->sfx = mapstart->sfx;
1117 map->sfy = mapstart->sfy;
1119 pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
1120 data = Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
1123 Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
1127 memset(map->width_of, 0, sizeof(map->width_of));
1129 // initialize as white texture with zero alpha
1131 while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
1133 if (bytesPerPixel == 4)
1143 map->start = mapidx * FONT_CHARS_PER_MAP;
1145 while(next->next && next->next->start < map->start)
1147 map->next = next->next;
1152 for (ch = map->start;
1153 ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
1156 FT_ULong glyphIndex;
1160 unsigned char *imagedata, *dst, *src;
1161 glyph_slot_t *mapglyph;
1163 int pad_l, pad_r, pad_t, pad_b;
1165 mapch = ch - map->start;
1167 if (developer_font.integer)
1168 Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
1171 if (gC >= FONT_CHARS_PER_LINE)
1173 gC -= FONT_CHARS_PER_LINE;
1177 imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
1178 imagedata += gpad_t * pitch + gpad_l * bytesPerPixel;
1179 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
1180 // we need the glyphIndex
1183 if (font->image_font && mapch == ch && img_fontmap[mapch])
1185 map->glyphs[mapch].image = true;
1188 glyphIndex = qFT_Get_Char_Index(face, ch);
1189 if (glyphIndex == 0)
1191 // by convention, 0 is the "missing-glyph"-glyph
1192 // try to load from a fallback font
1193 for(usefont = font->next; usefont != NULL; usefont = usefont->next)
1195 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
1198 face = usefont->face;
1199 glyphIndex = qFT_Get_Char_Index(face, ch);
1200 if (glyphIndex == 0)
1202 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1209 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1210 // now we let it use the "missing-glyph"-glyph
1220 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1223 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1224 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1229 glyph = face->glyph;
1230 bmp = &glyph->bitmap;
1235 if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
1236 Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
1237 if (w > map->glyphSize)
1238 w = map->glyphSize - gpad_l - gpad_r;
1239 if (h > map->glyphSize)
1243 switch (bmp->pixel_mode)
1245 case FT_PIXEL_MODE_MONO:
1246 if (developer_font.integer)
1247 Con_DPrint("glyphinfo: Pixel Mode: MONO\n");
1249 case FT_PIXEL_MODE_GRAY2:
1250 if (developer_font.integer)
1251 Con_DPrint("glyphinfo: Pixel Mode: GRAY2\n");
1253 case FT_PIXEL_MODE_GRAY4:
1254 if (developer_font.integer)
1255 Con_DPrint("glyphinfo: Pixel Mode: GRAY4\n");
1257 case FT_PIXEL_MODE_GRAY:
1258 if (developer_font.integer)
1259 Con_DPrint("glyphinfo: Pixel Mode: GRAY\n");
1262 if (developer_font.integer)
1263 Con_DPrintf("glyphinfo: Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1265 Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1268 for (y = 0; y < h; ++y)
1270 dst = imagedata + y * pitch;
1271 src = bmp->buffer + y * bmp->pitch;
1273 switch (bmp->pixel_mode)
1275 case FT_PIXEL_MODE_MONO:
1276 dst += bytesPerPixel - 1; // shift to alpha byte
1277 for (x = 0; x < bmp->width; x += 8)
1279 unsigned char ch = *src++;
1280 *dst = 255 * !!((ch & 0x80) >> 7); dst += bytesPerPixel;
1281 *dst = 255 * !!((ch & 0x40) >> 6); dst += bytesPerPixel;
1282 *dst = 255 * !!((ch & 0x20) >> 5); dst += bytesPerPixel;
1283 *dst = 255 * !!((ch & 0x10) >> 4); dst += bytesPerPixel;
1284 *dst = 255 * !!((ch & 0x08) >> 3); dst += bytesPerPixel;
1285 *dst = 255 * !!((ch & 0x04) >> 2); dst += bytesPerPixel;
1286 *dst = 255 * !!((ch & 0x02) >> 1); dst += bytesPerPixel;
1287 *dst = 255 * !!((ch & 0x01) >> 0); dst += bytesPerPixel;
1290 case FT_PIXEL_MODE_GRAY2:
1291 dst += bytesPerPixel - 1; // shift to alpha byte
1292 for (x = 0; x < bmp->width; x += 4)
1294 unsigned char ch = *src++;
1295 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1296 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1297 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1298 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1301 case FT_PIXEL_MODE_GRAY4:
1302 dst += bytesPerPixel - 1; // shift to alpha byte
1303 for (x = 0; x < bmp->width; x += 2)
1305 unsigned char ch = *src++;
1306 *dst = ( ((ch & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
1307 *dst = ( ((ch & 0x0F) ) * 0x11); dst += bytesPerPixel;
1310 case FT_PIXEL_MODE_GRAY:
1311 // in this case pitch should equal width
1312 for (tp = 0; tp < bmp->pitch; ++tp)
1313 dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
1315 //memcpy((void*)dst, (void*)src, bmp->pitch);
1316 //dst += bmp->pitch;
1327 Font_Postprocess(font, imagedata, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1329 // now fill map->glyphs[ch - map->start]
1330 mapglyph = &map->glyphs[mapch];
1334 // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1336 double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1337 //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1338 double advance = (glyph->advance.x / 64.0) / map->size;
1339 //double mWidth = (glyph->metrics.width >> 6) / map->size;
1340 //double mHeight = (glyph->metrics.height >> 6) / map->size;
1342 mapglyph->txmin = ( (double)(gC * map->glyphSize) + (double)(gpad_l - pad_l) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1343 mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1344 mapglyph->tymin = ( (double)(gR * map->glyphSize) + (double)(gpad_r - pad_r) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1345 mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1346 //mapglyph->vxmin = bearingX;
1347 //mapglyph->vxmax = bearingX + mWidth;
1348 mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
1349 mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
1350 //mapglyph->vymin = -bearingY;
1351 //mapglyph->vymax = mHeight - bearingY;
1352 mapglyph->vymin = (-glyph->bitmap_top - pad_t) / map->size;
1353 mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
1354 //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);
1355 //mapglyph->advance_x = advance * usefont->size;
1356 //mapglyph->advance_x = advance;
1357 mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1358 mapglyph->advance_y = 0;
1360 if (developer_font.integer)
1362 Con_DPrintf("glyphinfo: Glyph: %lu at (%i, %i)\n", (unsigned long)ch, gC, gR);
1363 Con_DPrintf("glyphinfo: %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1364 if (ch >= 32 && ch <= 128)
1365 Con_DPrintf("glyphinfo: Character: %c\n", (int)ch);
1366 Con_DPrintf("glyphinfo: Vertex info:\n");
1367 Con_DPrintf("glyphinfo: X: ( %f -- %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1368 Con_DPrintf("glyphinfo: Y: ( %f -- %f )\n", mapglyph->vymin, mapglyph->vymax);
1369 Con_DPrintf("glyphinfo: Texture info:\n");
1370 Con_DPrintf("glyphinfo: S: ( %f -- %f )\n", mapglyph->txmin, mapglyph->txmax);
1371 Con_DPrintf("glyphinfo: T: ( %f -- %f )\n", mapglyph->tymin, mapglyph->tymax);
1372 Con_DPrintf("glyphinfo: Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1375 map->glyphs[mapch].image = false;
1378 // create a texture from the data now
1380 if (developer_font.integer > 100)
1382 // LordHavoc: why are we writing this? And why not write it as TGA using the appropriate function?
1383 // view using `display -depth 8 -size 512x512 name_page.rgba` (be sure to use a correct -size parameter)
1384 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u.rgba", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1385 FS_WriteFile(map_identifier, data, pitch * FONT_CHAR_LINES * map->glyphSize);
1387 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1389 // probably use bytesPerPixel here instead?
1390 if (r_font_use_alpha_textures.integer)
1392 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1393 map->glyphSize * FONT_CHARS_PER_LINE,
1394 map->glyphSize * FONT_CHAR_LINES,
1395 data, TEXTYPE_ALPHA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL);
1397 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1398 map->glyphSize * FONT_CHARS_PER_LINE,
1399 map->glyphSize * FONT_CHAR_LINES,
1400 data, TEXTYPE_RGBA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL);
1406 // if the first try isn't successful, keep it with a broken texture
1407 // otherwise we retry to load it every single frame where ft2 rendering is used
1408 // this would be bad...
1409 // only `data' must be freed
1410 Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
1411 font->name, mapstart->size, mapidx);
1419 qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
1421 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1423 // the first map must have been loaded already
1424 if (!font->font_maps[map_index])
1426 return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
1429 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1431 while (start && start->start + FONT_CHARS_PER_MAP <= ch)
1432 start = start->next;
1433 if (start && start->start > ch)