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_postprocess_blur = {CVAR_SAVE, "r_font_postprocess_blur", "0", "font blur amount"};
36 cvar_t r_font_postprocess_outline = {CVAR_SAVE, "r_font_postprocess_outline", "0", "font outline amount"};
37 cvar_t r_font_disable_freetype = {CVAR_SAVE, "r_font_disable_freetype", "1", "disable freetype support for fonts entirely"};
38 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"};
39 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!"};
40 cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"};
41 cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */};
42 cvar_t r_font_kerning = {CVAR_SAVE, "r_font_kerning", "1", "Use kerning if available"};
43 cvar_t developer_font = {CVAR_SAVE, "developer_font", "0", "prints debug messages about fonts"};
46 ================================================================================
47 Function definitions. Taken from the freetype2 headers.
48 ================================================================================
53 (*qFT_Init_FreeType)( FT_Library *alibrary );
55 (*qFT_Done_FreeType)( FT_Library library );
58 (*qFT_New_Face)( FT_Library library,
59 const char* filepathname,
64 (*qFT_New_Memory_Face)( FT_Library library,
65 const FT_Byte* file_base,
70 (*qFT_Done_Face)( FT_Face face );
72 (*qFT_Select_Size)( FT_Face face,
73 FT_Int strike_index );
75 (*qFT_Request_Size)( FT_Face face,
76 FT_Size_Request req );
78 (*qFT_Set_Char_Size)( FT_Face face,
79 FT_F26Dot6 char_width,
80 FT_F26Dot6 char_height,
81 FT_UInt horz_resolution,
82 FT_UInt vert_resolution );
84 (*qFT_Set_Pixel_Sizes)( FT_Face face,
86 FT_UInt pixel_height );
88 (*qFT_Load_Glyph)( FT_Face face,
90 FT_Int32 load_flags );
92 (*qFT_Load_Char)( FT_Face face,
94 FT_Int32 load_flags );
96 (*qFT_Get_Char_Index)( FT_Face face,
99 (*qFT_Render_Glyph)( FT_GlyphSlot slot,
100 FT_Render_Mode render_mode );
101 FT_EXPORT( FT_Error )
102 (*qFT_Get_Kerning)( FT_Face face,
106 FT_Vector *akerning );
107 FT_EXPORT( FT_Error )
108 (*qFT_Attach_Stream)( FT_Face face,
109 FT_Open_Args* parameters );
111 ================================================================================
112 Support for dynamically loading the FreeType2 library
113 ================================================================================
116 static dllfunction_t ft2funcs[] =
118 {"FT_Init_FreeType", (void **) &qFT_Init_FreeType},
119 {"FT_Done_FreeType", (void **) &qFT_Done_FreeType},
120 //{"FT_New_Face", (void **) &qFT_New_Face},
121 {"FT_New_Memory_Face", (void **) &qFT_New_Memory_Face},
122 {"FT_Done_Face", (void **) &qFT_Done_Face},
123 {"FT_Select_Size", (void **) &qFT_Select_Size},
124 {"FT_Request_Size", (void **) &qFT_Request_Size},
125 {"FT_Set_Char_Size", (void **) &qFT_Set_Char_Size},
126 {"FT_Set_Pixel_Sizes", (void **) &qFT_Set_Pixel_Sizes},
127 {"FT_Load_Glyph", (void **) &qFT_Load_Glyph},
128 {"FT_Load_Char", (void **) &qFT_Load_Char},
129 {"FT_Get_Char_Index", (void **) &qFT_Get_Char_Index},
130 {"FT_Render_Glyph", (void **) &qFT_Render_Glyph},
131 {"FT_Get_Kerning", (void **) &qFT_Get_Kerning},
132 {"FT_Attach_Stream", (void **) &qFT_Attach_Stream},
136 /// Handle for FreeType2 DLL
137 static dllhandle_t ft2_dll = NULL;
139 /// Memory pool for fonts
140 static mempool_t *font_mempool= NULL;
141 static rtexturepool_t *font_texturepool = NULL;
143 /// FreeType library handle
144 static FT_Library font_ft2lib = NULL;
146 #define POSTPROCESS_MAXRADIUS 8
149 unsigned char *buf, *buf2;
150 int bufsize, bufwidth, bufheight, bufpitch;
151 double blur, outline;
152 int padding, blurpadding, outlinepadding;
153 unsigned char circlematrix[2*POSTPROCESS_MAXRADIUS+1][2*POSTPROCESS_MAXRADIUS+1];
154 unsigned char gausstable[2*POSTPROCESS_MAXRADIUS+1];
157 static font_postprocess_t pp = {};
163 Unload the FreeType2 DLL
166 void Font_CloseLibrary (void)
169 Mem_FreePool(&font_mempool);
170 if (font_texturepool)
171 R_FreeTexturePool(&font_texturepool);
172 if (font_ft2lib && qFT_Done_FreeType)
174 qFT_Done_FreeType(font_ft2lib);
177 Sys_UnloadLibrary (&ft2_dll);
185 Try to load the FreeType2 DLL
188 qboolean Font_OpenLibrary (void)
190 const char* dllnames [] =
195 #elif defined(MACOSX)
196 "libfreetype.6.dylib",
205 if (r_font_disable_freetype.integer)
213 if (!Sys_LoadLibrary (dllnames, &ft2_dll, ft2funcs))
222 Initialize the freetype2 font subsystem
226 void font_start(void)
228 if (!Font_OpenLibrary())
231 if (qFT_Init_FreeType(&font_ft2lib))
233 Con_Print("ERROR: Failed to initialize the FreeType2 library!\n");
238 font_mempool = Mem_AllocPool("FONT", 0, NULL);
241 Con_Print("ERROR: Failed to allocate FONT memory pool!\n");
246 font_texturepool = R_AllocTexturePool();
247 if (!font_texturepool)
249 Con_Print("ERROR: Failed to allocate FONT texture pool!\n");
255 void font_shutdown(void)
258 for (i = 0; i < MAX_FONTS; ++i)
262 Font_UnloadFont(dp_fonts[i].ft2);
263 dp_fonts[i].ft2 = NULL;
269 void font_newmap(void)
275 Cvar_RegisterVariable(&r_font_postprocess_blur);
276 Cvar_RegisterVariable(&r_font_postprocess_outline);
277 Cvar_RegisterVariable(&r_font_disable_freetype);
278 Cvar_RegisterVariable(&r_font_use_alpha_textures);
279 Cvar_RegisterVariable(&r_font_size_snapping);
280 Cvar_RegisterVariable(&r_font_hinting);
281 Cvar_RegisterVariable(&r_font_antialias);
282 Cvar_RegisterVariable(&r_font_kerning);
283 Cvar_RegisterVariable(&developer_font);
284 // let's open it at startup already
289 ================================================================================
290 Implementation of a more or less lazy font loading and rendering code.
291 ================================================================================
294 #include "ft2_fontdefs.h"
296 ft2_font_t *Font_Alloc(void)
300 return Mem_Alloc(font_mempool, sizeof(ft2_font_t));
303 qboolean Font_Attach(ft2_font_t *font, ft2_attachment_t *attachment)
305 ft2_attachment_t *na;
307 font->attachmentcount++;
308 na = (ft2_attachment_t*)Mem_Alloc(font_mempool, sizeof(font->attachments[0]) * font->attachmentcount);
311 if (font->attachments && font->attachmentcount > 1)
313 memcpy(na, font->attachments, sizeof(font->attachments[0]) * (font->attachmentcount - 1));
314 Mem_Free(font->attachments);
316 memcpy(na + sizeof(font->attachments[0]) * (font->attachmentcount - 1), attachment, sizeof(*attachment));
317 font->attachments = na;
321 float Font_VirtualToRealSize(float sz)
327 vw = ((vid.width > 0) ? vid.width : vid_width.value);
328 vh = ((vid.height > 0) ? vid.height : vid_height.value);
329 // now try to scale to our actual size:
330 sn = sz * vh / vid_conheight.value;
332 if ( sn - (float)si >= 0.5 )
337 float Font_SnapTo(float val, float snapwidth)
339 return floor(val / snapwidth + 0.5f) * snapwidth;
342 static qboolean Font_LoadFile(const char *name, int _face, ft2_font_t *font);
343 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only);
344 qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt)
347 ft2_font_t *ft2, *fbfont, *fb;
356 // check if a fallback font has been specified, if it has been, and the
357 // font fails to load, use the image font as main font
358 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
360 if (dpfnt->fallbacks[i][0])
364 if (!Font_LoadFile(name, dpfnt->req_face, ft2))
366 if (i >= MAX_FONT_FALLBACKS)
372 strlcpy(ft2->name, name, sizeof(ft2->name));
373 ft2->image_font = true;
374 ft2->has_kerning = false;
378 ft2->image_font = false;
381 // attempt to load fallback fonts:
383 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
385 if (!dpfnt->fallbacks[i][0])
387 if (! (fb = Font_Alloc()) )
389 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
392 if (!Font_LoadFile(dpfnt->fallbacks[i], dpfnt->fallback_faces[i], fb))
394 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
399 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
401 if (Font_LoadSize(fb, Font_VirtualToRealSize(dpfnt->req_sizes[s]), true))
406 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
411 // at least one size of the fallback font loaded successfully
417 if (fbfont == ft2 && ft2->image_font)
419 // no fallbacks were loaded successfully:
426 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
428 if (Font_LoadSize(ft2, Font_VirtualToRealSize(dpfnt->req_sizes[s]), false))
433 // loading failed for every requested size
434 Font_UnloadFont(ft2);
440 //Con_Printf("%i sizes loaded\n", count);
445 static qboolean Font_LoadFile(const char *name, int _face, ft2_font_t *font)
448 char filename[MAX_QPATH];
452 fs_offset_t datasize;
454 memset(font, 0, sizeof(*font));
456 if (!Font_OpenLibrary())
458 if (!r_font_disable_freetype.integer)
460 Con_Printf("WARNING: can't open load font %s\n"
461 "You need the FreeType2 DLL to load font files\n",
467 namelen = strlen(name);
469 memcpy(filename, name, namelen);
470 memcpy(filename + namelen, ".ttf", 5);
471 data = FS_LoadFile(filename, font_mempool, false, &datasize);
474 memcpy(filename + namelen, ".otf", 5);
475 data = FS_LoadFile(filename, font_mempool, false, &datasize);
479 ft2_attachment_t afm;
481 memcpy(filename + namelen, ".pfb", 5);
482 data = FS_LoadFile(filename, font_mempool, false, &datasize);
486 memcpy(filename + namelen, ".afm", 5);
487 afm.data = FS_LoadFile(filename, font_mempool, false, &afm.size);
490 Font_Attach(font, &afm);
496 // FS_LoadFile being not-quiet should print an error :)
499 Con_Printf("Loading font %s face %i...\n", filename, _face);
501 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
502 if (status && _face != 0)
504 Con_Printf("Failed to load face %i of %s. Falling back to face 0\n", _face, name);
506 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, 0, (FT_Face*)&font->face);
510 Con_Printf("ERROR: can't create face for %s\n"
511 "Error %i\n", // TODO: error strings
513 Font_UnloadFont(font);
517 // add the attachments
518 for (i = 0; i < font->attachmentcount; ++i)
521 memset(&args, 0, sizeof(args));
522 args.flags = FT_OPEN_MEMORY;
523 args.memory_base = (const FT_Byte*)font->attachments[i].data;
524 args.memory_size = font->attachments[i].size;
525 if (qFT_Attach_Stream(font->face, &args))
526 Con_Printf("Failed to add attachment %u to %s\n", (unsigned)i, font->name);
529 memcpy(font->name, name, namelen+1);
530 font->image_font = false;
531 font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
535 void Font_Postprocess_Update(int bpp, int w, int h)
537 qboolean need_gauss, need_circle;
539 double gausstable[2*POSTPROCESS_MAXRADIUS+1];
540 if(!pp.buf || pp.blur != r_font_postprocess_blur.value)
542 if(!pp.buf || pp.outline != r_font_postprocess_outline.value)
544 pp.blur = r_font_postprocess_blur.value;
545 pp.outline = r_font_postprocess_outline.value;
546 pp.outlinepadding = bound(0, ceil(pp.outline), POSTPROCESS_MAXRADIUS);
547 pp.blurpadding = bound(0, ceil(pp.blur), POSTPROCESS_MAXRADIUS);
548 pp.padding = pp.blurpadding + pp.outlinepadding;
552 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
553 sum += (gausstable[POSTPROCESS_MAXRADIUS+x] = (pp.blur > 0 ? exp(-(x*x)/(pp.blur*pp.blur * 2)) : (x == 0)));
554 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
555 pp.gausstable[POSTPROCESS_MAXRADIUS+x] = floor(gausstable[POSTPROCESS_MAXRADIUS+x] / sum * 255 + 0.5);
559 for(y = -POSTPROCESS_MAXRADIUS; y <= POSTPROCESS_MAXRADIUS; ++y)
560 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
562 double d = pp.outline + 1 - sqrt(x*x + y*y);
563 pp.circlematrix[POSTPROCESS_MAXRADIUS+y][POSTPROCESS_MAXRADIUS+x] = (d >= 1) ? 255 : (d <= 0) ? 0 : floor(d * 255 + 0.5);
566 pp.bufwidth = w + 2 * pp.padding;
567 pp.bufheight = h + 2 * pp.padding;
568 pp.bufpitch = pp.bufwidth;
569 needed = pp.bufwidth * pp.bufheight;
570 if(!pp.buf || pp.bufsize < needed * 2)
574 pp.bufsize = needed * 4;
575 pp.buf = Mem_Alloc(font_mempool, pp.bufsize);
576 pp.buf2 = pp.buf + needed;
580 void Font_Postprocess(unsigned char *imagedata, int pitch, int bpp, int w, int h, int *pad_l, int *pad_r, int *pad_t, int *pad_b)
583 Font_Postprocess_Update(bpp, w, h);
588 // perform operation, not exceeding the passed padding values,
589 // but possibly reducing them
590 *pad_l = min(*pad_l, pp.padding);
591 *pad_r = min(*pad_r, pp.padding);
592 *pad_t = min(*pad_t, pp.padding);
593 *pad_b = min(*pad_b, pp.padding);
595 // calculate gauss table
597 // outline the font (RGBA only)
598 if(bpp == 4) // we can only do this in BGRA
600 // this is like mplayer subtitle rendering
601 // bbuffer, bitmap buffer: this is our font
602 // abuffer, alpha buffer: this is pp.buf
603 // tmp: this is pp.buf2
605 // create outline buffer
606 memset(pp.buf, 0, pp.bufwidth * pp.bufheight);
607 for(y = -*pad_t; y < h + *pad_b; ++y)
608 for(x = -*pad_l; x < w + *pad_r; ++x)
610 int x1 = max(-x, -pp.outlinepadding);
611 int y1 = max(-y, -pp.outlinepadding);
612 int x2 = min(pp.outlinepadding, w-1-x);
613 int y2 = min(pp.outlinepadding, h-1-y);
617 for(my = y1; my <= y2; ++my)
618 for(mx = x1; mx <= x2; ++mx)
620 cur = pp.circlematrix[POSTPROCESS_MAXRADIUS+my][POSTPROCESS_MAXRADIUS+mx] * (int)imagedata[(x+mx) * bpp + pitch * (y+my) + (bpp - 1)];
624 pp.buf[((x + pp.padding) + pp.bufpitch * (y + pp.padding))] = (highest + 128) / 255;
627 // blur the outline buffer
631 for(y = 0; y < pp.bufheight; ++y)
632 for(x = 0; x < pp.bufwidth; ++x)
634 int x1 = max(-x, -pp.blurpadding);
635 int x2 = min(pp.blurpadding, pp.bufwidth-1-x);
638 for(mx = x1; mx <= x2; ++mx)
639 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+mx] * (int)pp.buf[(x+mx) + pp.bufpitch * y];
640 pp.buf2[x + pp.bufpitch * y] = blurred / 255;
644 for(y = 0; y < pp.bufheight; ++y)
645 for(x = 0; x < pp.bufwidth; ++x)
647 int y1 = max(-y, -pp.blurpadding);
648 int y2 = min(pp.blurpadding, pp.bufheight-1-y);
651 for(my = y1; my <= y2; ++my)
652 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+my] * (int)pp.buf2[x + pp.bufpitch * (y+my)];
653 pp.buf[x + pp.bufpitch * y] = blurred / 255;
657 // paste the outline below the font
658 for(y = -*pad_t; y < h + *pad_b; ++y)
659 for(x = -*pad_l; x < w + *pad_r; ++x)
661 unsigned char outlinealpha = pp.buf[(x + pp.padding) + pp.bufpitch * (y + pp.padding)];
664 unsigned char oldalpha = imagedata[x * bpp + pitch * y + (bpp - 1)];
665 // a' = 1 - (1 - a1) (1 - a2)
666 unsigned char newalpha = 255 - ((255 - (int)outlinealpha) * (255 - (int)oldalpha)) / 255; // this is >= oldalpha
667 // c' = (a2 c2 - a1 a2 c1 + a1 c1) / a' = (a2 c2 + a1 (1 - a2) c1) / a'
668 unsigned char oldfactor = (255 * (int)oldalpha) / newalpha;
669 //unsigned char outlinefactor = ((255 - oldalpha) * (int)outlinealpha) / newalpha;
671 for(i = 0; i < bpp-1; ++i)
673 unsigned char c = imagedata[x * bpp + pitch * y + i];
674 c = (c * (int)oldfactor) / 255 /* + outlinecolor[i] * (int)outlinefactor */;
675 imagedata[x * bpp + pitch * y + i] = c;
677 imagedata[x * bpp + pitch * y + (bpp - 1)] = newalpha;
684 // just calculate parameters
685 *pad_l = *pad_r = *pad_t = *pad_b = pp.padding;
689 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size);
690 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
691 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only)
694 ft2_font_map_t *fmap, temp;
695 int gpad_l, gpad_r, gpad_t, gpad_b;
697 if (!(size > 0.001f && size < 1000.0f))
702 if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
705 for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
707 if (!font->font_maps[map_index])
709 // if a similar size has already been loaded, ignore this one
710 //abs(font->font_maps[map_index]->size - size) < 4
711 if (font->font_maps[map_index]->size == size)
715 if (map_index >= MAX_FONT_SIZES)
720 if (font->image_font)
721 fontface = (FT_Face)font->next->face;
723 fontface = (FT_Face)font->face;
724 return (Font_SearchSize(font, fontface, size) > 0);
727 Font_Postprocess(NULL, 0, 4, size*2, size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
729 memset(&temp, 0, sizeof(temp));
731 temp.glyphSize = CeilPowerOf2(size*2 + max(gpad_l + gpad_r, gpad_t + gpad_b));
732 temp.sfx = (1.0/64.0)/(double)size;
733 temp.sfy = (1.0/64.0)/(double)size;
734 temp.intSize = -1; // negative value: LoadMap must search now :)
735 if (!Font_LoadMap(font, &temp, 0, &fmap))
737 Con_Printf("ERROR: can't load the first character map for %s\n"
740 Font_UnloadFont(font);
743 font->font_maps[map_index] = temp.next;
745 fmap->sfx = temp.sfx;
746 fmap->sfy = temp.sfy;
748 // load the default kerning vector:
749 if (font->has_kerning)
753 for (l = 0; l < 256; ++l)
755 for (r = 0; r < 256; ++r)
758 ul = qFT_Get_Char_Index(font->face, l);
759 ur = qFT_Get_Char_Index(font->face, r);
760 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
762 fmap->kerning.kerning[l][r][0] = 0;
763 fmap->kerning.kerning[l][r][1] = 0;
767 fmap->kerning.kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
768 fmap->kerning.kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
776 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
781 int matchsize = -10000;
783 float fsize_x, fsize_y;
784 ft2_font_map_t **maps = font->font_maps;
786 fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value;
788 fsize_x = *outw * vid.width / vid_conwidth.value;
790 fsize_y = *outh * vid.height / vid_conheight.value;
795 fsize_x = fsize_y = 16;
805 for (m = 0; m < MAX_FONT_SIZES; ++m)
809 // "round up" to the bigger size if two equally-valued matches exist
810 nval = 0.5 * (abs(maps[m]->size - fsize_x) + abs(maps[m]->size - fsize_y));
811 if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
815 matchsize = maps[m]->size;
816 if (value == 0) // there is no better match
820 if (value <= r_font_size_snapping.value)
822 // do NOT keep the aspect for perfect rendering
823 if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
824 if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width;
829 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
831 if (index < 0 || index >= MAX_FONT_SIZES)
833 return font->font_maps[index];
836 static qboolean Font_SetSize(ft2_font_t *font, float w, float h)
838 if (font->currenth == h &&
839 ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
840 font->currentw == w)) // same size has been requested
844 // sorry, but freetype doesn't seem to care about other sizes
847 if (font->image_font)
849 if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
854 if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
862 qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
864 ft2_font_map_t *fmap;
865 if (!font->has_kerning || !r_font_kerning.integer)
867 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
869 fmap = font->font_maps[map_index];
872 if (left < 256 && right < 256)
874 //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
875 // quick-kerning, be aware of the size: scale it
876 if (outx) *outx = fmap->kerning.kerning[left][right][0];// * (w / (float)fmap->size);
877 if (outy) *outy = fmap->kerning.kerning[left][right][1];// * (h / (float)fmap->size);
885 //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
887 if (!Font_SetSize(font, w, h))
889 // this deserves an error message
890 Con_Printf("Failed to get kerning for %s\n", font->name);
893 ul = qFT_Get_Char_Index(font->face, left);
894 ur = qFT_Get_Char_Index(font->face, right);
895 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
897 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);
898 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);
902 if (!Font_SetSize(font, fmap->intSize, fmap->intSize))
904 // this deserves an error message
905 Con_Printf("Failed to get kerning for %s\n", font->name);
908 ul = qFT_Get_Char_Index(font->face, left);
909 ur = qFT_Get_Char_Index(font->face, right);
910 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
912 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);// * (w / (float)fmap->size);
913 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);// * (h / (float)fmap->size);
920 qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
922 return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
925 static void UnloadMapRec(ft2_font_map_t *map)
929 R_FreeTexture(map->texture);
933 UnloadMapRec(map->next);
937 void Font_UnloadFont(ft2_font_t *font)
940 if (font->attachments && font->attachmentcount)
942 Mem_Free(font->attachments);
943 font->attachmentcount = 0;
944 font->attachments = NULL;
946 for (i = 0; i < MAX_FONT_SIZES; ++i)
948 if (font->font_maps[i])
950 UnloadMapRec(font->font_maps[i]);
951 font->font_maps[i] = NULL;
958 qFT_Done_Face((FT_Face)font->face);
964 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size)
966 float intSize = size;
969 if (!Font_SetSize(font, intSize, intSize))
971 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, size, intSize);
974 if ((fontface->size->metrics.height>>6) <= size)
978 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, size);
985 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
987 char map_identifier[MAX_QPATH];
988 unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
994 int gpad_l, gpad_r, gpad_t, gpad_b;
997 int gR, gC; // glyph position: row and column
999 ft2_font_map_t *map, *next;
1000 ft2_font_t *usefont;
1004 int bytesPerPixel = 4; // change the conversion loop too if you change this!
1009 if (r_font_use_alpha_textures.integer)
1012 if (font->image_font)
1013 fontface = (FT_Face)font->next->face;
1015 fontface = (FT_Face)font->face;
1017 switch(r_font_antialias.integer)
1020 switch(r_font_hinting.integer)
1023 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1027 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1031 load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1037 switch(r_font_hinting.integer)
1040 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1043 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
1046 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1050 load_flags = FT_LOAD_TARGET_NORMAL;
1056 //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
1058 if (font->image_font && mapstart->intSize < 0)
1059 mapstart->intSize = mapstart->size;
1060 if (mapstart->intSize < 0)
1063 mapstart->intSize = mapstart->size;
1066 if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1068 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
1071 if ((fontface->size->metrics.height>>6) <= mapstart->size)
1073 if (mapstart->intSize < 2)
1075 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
1078 --mapstart->intSize;
1081 if ((mapstart->intSize = Font_SearchSize(font, fontface, mapstart->size)) <= 0)
1083 Con_DPrintf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
1086 if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1088 Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
1092 map = Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
1095 Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
1099 Font_Postprocess(NULL, 0, bytesPerPixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
1101 // copy over the information
1102 map->size = mapstart->size;
1103 map->intSize = mapstart->intSize;
1104 map->glyphSize = mapstart->glyphSize;
1105 map->sfx = mapstart->sfx;
1106 map->sfy = mapstart->sfy;
1108 pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
1109 data = Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
1112 Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
1116 memset(map->width_of, 0, sizeof(map->width_of));
1118 // initialize as white texture with zero alpha
1120 while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
1122 if (bytesPerPixel == 4)
1132 map->start = mapidx * FONT_CHARS_PER_MAP;
1134 while(next->next && next->next->start < map->start)
1136 map->next = next->next;
1141 for (ch = map->start;
1142 ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
1145 FT_ULong glyphIndex;
1149 unsigned char *imagedata, *dst, *src;
1150 glyph_slot_t *mapglyph;
1152 int pad_l, pad_r, pad_t, pad_b;
1154 mapch = ch - map->start;
1156 if (developer_font.integer)
1157 Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
1160 if (gC >= FONT_CHARS_PER_LINE)
1162 gC -= FONT_CHARS_PER_LINE;
1166 imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
1167 imagedata += gpad_t * pitch + gpad_l * bytesPerPixel;
1168 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
1169 // we need the glyphIndex
1172 if (font->image_font && mapch == ch && img_fontmap[mapch])
1174 map->glyphs[mapch].image = true;
1177 glyphIndex = qFT_Get_Char_Index(face, ch);
1178 if (glyphIndex == 0)
1180 // by convention, 0 is the "missing-glyph"-glyph
1181 // try to load from a fallback font
1182 for(usefont = font->next; usefont != NULL; usefont = usefont->next)
1184 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
1187 face = usefont->face;
1188 glyphIndex = qFT_Get_Char_Index(face, ch);
1189 if (glyphIndex == 0)
1191 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1198 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1199 // now we let it use the "missing-glyph"-glyph
1209 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1212 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1213 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1218 glyph = face->glyph;
1219 bmp = &glyph->bitmap;
1224 if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
1225 Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
1226 if (w > map->glyphSize)
1227 w = map->glyphSize - gpad_l - gpad_r;
1228 if (h > map->glyphSize)
1232 switch (bmp->pixel_mode)
1234 case FT_PIXEL_MODE_MONO:
1235 if (developer_font.integer)
1236 Con_DPrint("glyphinfo: Pixel Mode: MONO\n");
1238 case FT_PIXEL_MODE_GRAY2:
1239 if (developer_font.integer)
1240 Con_DPrint("glyphinfo: Pixel Mode: GRAY2\n");
1242 case FT_PIXEL_MODE_GRAY4:
1243 if (developer_font.integer)
1244 Con_DPrint("glyphinfo: Pixel Mode: GRAY4\n");
1246 case FT_PIXEL_MODE_GRAY:
1247 if (developer_font.integer)
1248 Con_DPrint("glyphinfo: Pixel Mode: GRAY\n");
1251 if (developer_font.integer)
1252 Con_DPrintf("glyphinfo: Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1254 Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1257 for (y = 0; y < h; ++y)
1259 dst = imagedata + y * pitch;
1260 src = bmp->buffer + y * bmp->pitch;
1262 switch (bmp->pixel_mode)
1264 case FT_PIXEL_MODE_MONO:
1265 dst += bytesPerPixel - 1; // shift to alpha byte
1266 for (x = 0; x < bmp->width; x += 8)
1268 unsigned char ch = *src++;
1269 *dst = 255 * !!((ch & 0x80) >> 7); dst += bytesPerPixel;
1270 *dst = 255 * !!((ch & 0x40) >> 6); dst += bytesPerPixel;
1271 *dst = 255 * !!((ch & 0x20) >> 5); dst += bytesPerPixel;
1272 *dst = 255 * !!((ch & 0x10) >> 4); dst += bytesPerPixel;
1273 *dst = 255 * !!((ch & 0x08) >> 3); dst += bytesPerPixel;
1274 *dst = 255 * !!((ch & 0x04) >> 2); dst += bytesPerPixel;
1275 *dst = 255 * !!((ch & 0x02) >> 1); dst += bytesPerPixel;
1276 *dst = 255 * !!((ch & 0x01) >> 0); dst += bytesPerPixel;
1279 case FT_PIXEL_MODE_GRAY2:
1280 dst += bytesPerPixel - 1; // shift to alpha byte
1281 for (x = 0; x < bmp->width; x += 4)
1283 unsigned char ch = *src++;
1284 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1285 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1286 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1287 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1290 case FT_PIXEL_MODE_GRAY4:
1291 dst += bytesPerPixel - 1; // shift to alpha byte
1292 for (x = 0; x < bmp->width; x += 2)
1294 unsigned char ch = *src++;
1295 *dst = ( ((ch & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
1296 *dst = ( ((ch & 0x0F) ) * 0x11); dst += bytesPerPixel;
1299 case FT_PIXEL_MODE_GRAY:
1300 // in this case pitch should equal width
1301 for (tp = 0; tp < bmp->pitch; ++tp)
1302 dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
1304 //memcpy((void*)dst, (void*)src, bmp->pitch);
1305 //dst += bmp->pitch;
1316 Font_Postprocess(imagedata, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1318 // now fill map->glyphs[ch - map->start]
1319 mapglyph = &map->glyphs[mapch];
1323 // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1325 double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1326 //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1327 double advance = (glyph->advance.x / 64.0) / map->size;
1328 //double mWidth = (glyph->metrics.width >> 6) / map->size;
1329 //double mHeight = (glyph->metrics.height >> 6) / map->size;
1331 mapglyph->txmin = ( (double)(gC * map->glyphSize) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1332 mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1333 mapglyph->tymin = ( (double)(gR * map->glyphSize) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1334 mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1335 //mapglyph->vxmin = bearingX;
1336 //mapglyph->vxmax = bearingX + mWidth;
1337 mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
1338 mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
1339 //mapglyph->vymin = -bearingY;
1340 //mapglyph->vymax = mHeight - bearingY;
1341 mapglyph->vymin = (-glyph->bitmap_top - pad_t) / map->size;
1342 mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
1343 //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);
1344 //mapglyph->advance_x = advance * usefont->size;
1345 //mapglyph->advance_x = advance;
1346 mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1347 mapglyph->advance_y = 0;
1349 if (developer_font.integer)
1351 Con_DPrintf("glyphinfo: Glyph: %lu at (%i, %i)\n", (unsigned long)ch, gC, gR);
1352 Con_DPrintf("glyphinfo: %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1353 if (ch >= 32 && ch <= 128)
1354 Con_DPrintf("glyphinfo: Character: %c\n", (int)ch);
1355 Con_DPrintf("glyphinfo: Vertex info:\n");
1356 Con_DPrintf("glyphinfo: X: ( %f -- %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1357 Con_DPrintf("glyphinfo: Y: ( %f -- %f )\n", mapglyph->vymin, mapglyph->vymax);
1358 Con_DPrintf("glyphinfo: Texture info:\n");
1359 Con_DPrintf("glyphinfo: S: ( %f -- %f )\n", mapglyph->txmin, mapglyph->txmax);
1360 Con_DPrintf("glyphinfo: T: ( %f -- %f )\n", mapglyph->tymin, mapglyph->tymax);
1361 Con_DPrintf("glyphinfo: Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1364 map->glyphs[mapch].image = false;
1367 // create a texture from the data now
1369 if (developer_font.integer > 100)
1371 // LordHavoc: why are we writing this? And why not write it as TGA using the appropriate function?
1372 // view using `display -depth 8 -size 512x512 name_page.rgba` (be sure to use a correct -size parameter)
1373 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u.rgba", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1374 FS_WriteFile(map_identifier, data, pitch * FONT_CHAR_LINES * map->glyphSize);
1376 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1378 // probably use bytesPerPixel here instead?
1379 if (r_font_use_alpha_textures.integer)
1381 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1382 map->glyphSize * FONT_CHARS_PER_LINE,
1383 map->glyphSize * FONT_CHAR_LINES,
1384 data, TEXTYPE_ALPHA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL);
1386 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1387 map->glyphSize * FONT_CHARS_PER_LINE,
1388 map->glyphSize * FONT_CHAR_LINES,
1389 data, TEXTYPE_RGBA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL);
1395 // if the first try isn't successful, keep it with a broken texture
1396 // otherwise we retry to load it every single frame where ft2 rendering is used
1397 // this would be bad...
1398 // only `data' must be freed
1399 Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
1400 font->name, mapstart->size, mapidx);
1408 qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
1410 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1412 // the first map must have been loaded already
1413 if (!font->font_maps[map_index])
1415 return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
1418 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1420 while (start && start->start + FONT_CHARS_PER_MAP <= ch)
1421 start = start->next;
1422 if (start && start->start > ch)