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, blurpadding, outlinepadding;
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 = bound(0, ceil(pp.outline) + ceil(max(max(pp.shadowx, pp.shadowy), max(-pp.shadowx, -pp.shadowy))), POSTPROCESS_MAXRADIUS);
545 pp.blurpadding = bound(0, ceil(pp.blur) + ceil(max(pp.shadowz, -pp.shadowz)), POSTPROCESS_MAXRADIUS);
546 // FIXME for high values of shadow, we can do better by doing different x and y padding...
547 pp.padding = pp.blurpadding + pp.outlinepadding;
551 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
552 sum += (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)));
553 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
554 pp.gausstable[POSTPROCESS_MAXRADIUS+x] = floor(gausstable[POSTPROCESS_MAXRADIUS+x] / sum * 255 + 0.5);
558 for(y = -POSTPROCESS_MAXRADIUS; y <= POSTPROCESS_MAXRADIUS; ++y)
559 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
561 float d = pp.outline + 1 - sqrt(pow(x + pp.shadowx, 2) + pow(y + pp.shadowy, 2));
562 pp.circlematrix[POSTPROCESS_MAXRADIUS+y][POSTPROCESS_MAXRADIUS+x] = (d >= 1) ? 255 : (d <= 0) ? 0 : floor(d * 255 + 0.5);
565 pp.bufwidth = w + 2 * pp.padding;
566 pp.bufheight = h + 2 * pp.padding;
567 pp.bufpitch = pp.bufwidth;
568 needed = pp.bufwidth * pp.bufheight;
569 if(!pp.buf || pp.bufsize < needed * 2)
573 pp.bufsize = needed * 4;
574 pp.buf = Mem_Alloc(font_mempool, pp.bufsize);
575 pp.buf2 = pp.buf + needed;
579 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)
582 Font_Postprocess_Update(fnt, bpp, w, h);
587 // perform operation, not exceeding the passed padding values,
588 // but possibly reducing them
589 *pad_l = min(*pad_l, pp.padding);
590 *pad_r = min(*pad_r, pp.padding);
591 *pad_t = min(*pad_t, pp.padding);
592 *pad_b = min(*pad_b, pp.padding);
594 // calculate gauss table
596 // outline the font (RGBA only)
597 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
599 // this is like mplayer subtitle rendering
600 // bbuffer, bitmap buffer: this is our font
601 // abuffer, alpha buffer: this is pp.buf
602 // tmp: this is pp.buf2
604 // create outline buffer
605 memset(pp.buf, 0, pp.bufwidth * pp.bufheight);
606 for(y = -*pad_t; y < h + *pad_b; ++y)
607 for(x = -*pad_l; x < w + *pad_r; ++x)
609 int x1 = max(-x, -pp.outlinepadding);
610 int y1 = max(-y, -pp.outlinepadding);
611 int x2 = min(pp.outlinepadding, w-1-x);
612 int y2 = min(pp.outlinepadding, h-1-y);
616 for(my = y1; my <= y2; ++my)
617 for(mx = x1; mx <= x2; ++mx)
619 cur = pp.circlematrix[POSTPROCESS_MAXRADIUS+my][POSTPROCESS_MAXRADIUS+mx] * (int)imagedata[(x+mx) * bpp + pitch * (y+my) + (bpp - 1)];
623 pp.buf[((x + pp.padding) + pp.bufpitch * (y + pp.padding))] = (highest + 128) / 255;
626 // blur the outline buffer
627 if(pp.blur > 0 || pp.shadowz != 0)
630 for(y = 0; y < pp.bufheight; ++y)
631 for(x = 0; x < pp.bufwidth; ++x)
633 int x1 = max(-x, -pp.blurpadding);
634 int x2 = min(pp.blurpadding, pp.bufwidth-1-x);
637 for(mx = x1; mx <= x2; ++mx)
638 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+mx] * (int)pp.buf[(x+mx) + pp.bufpitch * y];
639 pp.buf2[x + pp.bufpitch * y] = blurred / 255;
643 for(y = 0; y < pp.bufheight; ++y)
644 for(x = 0; x < pp.bufwidth; ++x)
646 int y1 = max(-y, -pp.blurpadding);
647 int y2 = min(pp.blurpadding, pp.bufheight-1-y);
650 for(my = y1; my <= y2; ++my)
651 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+my] * (int)pp.buf2[x + pp.bufpitch * (y+my)];
652 pp.buf[x + pp.bufpitch * y] = blurred / 255;
656 // paste the outline below the font
657 for(y = -*pad_t; y < h + *pad_b; ++y)
658 for(x = -*pad_l; x < w + *pad_r; ++x)
660 unsigned char outlinealpha = pp.buf[(x + pp.padding) + pp.bufpitch * (y + pp.padding)];
663 unsigned char oldalpha = imagedata[x * bpp + pitch * y + (bpp - 1)];
664 // a' = 1 - (1 - a1) (1 - a2)
665 unsigned char newalpha = 255 - ((255 - (int)outlinealpha) * (255 - (int)oldalpha)) / 255; // this is >= oldalpha
666 // c' = (a2 c2 - a1 a2 c1 + a1 c1) / a' = (a2 c2 + a1 (1 - a2) c1) / a'
667 unsigned char oldfactor = (255 * (int)oldalpha) / newalpha;
668 //unsigned char outlinefactor = ((255 - oldalpha) * (int)outlinealpha) / newalpha;
670 for(i = 0; i < bpp-1; ++i)
672 unsigned char c = imagedata[x * bpp + pitch * y + i];
673 c = (c * (int)oldfactor) / 255 /* + outlinecolor[i] * (int)outlinefactor */;
674 imagedata[x * bpp + pitch * y + i] = c;
676 imagedata[x * bpp + pitch * y + (bpp - 1)] = newalpha;
683 // just calculate parameters
684 *pad_l = *pad_r = *pad_t = *pad_b = pp.padding;
688 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size);
689 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
690 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only)
693 ft2_font_map_t *fmap, temp;
694 int gpad_l, gpad_r, gpad_t, gpad_b;
696 if (!(size > 0.001f && size < 1000.0f))
701 if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
704 for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
706 if (!font->font_maps[map_index])
708 // if a similar size has already been loaded, ignore this one
709 //abs(font->font_maps[map_index]->size - size) < 4
710 if (font->font_maps[map_index]->size == size)
714 if (map_index >= MAX_FONT_SIZES)
719 if (font->image_font)
720 fontface = (FT_Face)font->next->face;
722 fontface = (FT_Face)font->face;
723 return (Font_SearchSize(font, fontface, size) > 0);
726 Font_Postprocess(font, NULL, 0, 4, size*2, size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
728 memset(&temp, 0, sizeof(temp));
730 temp.glyphSize = CeilPowerOf2(size*2 + max(gpad_l + gpad_r, gpad_t + gpad_b));
731 temp.sfx = (1.0/64.0)/(double)size;
732 temp.sfy = (1.0/64.0)/(double)size;
733 temp.intSize = -1; // negative value: LoadMap must search now :)
734 if (!Font_LoadMap(font, &temp, 0, &fmap))
736 Con_Printf("ERROR: can't load the first character map for %s\n"
739 Font_UnloadFont(font);
742 font->font_maps[map_index] = temp.next;
744 fmap->sfx = temp.sfx;
745 fmap->sfy = temp.sfy;
747 // load the default kerning vector:
748 if (font->has_kerning)
752 for (l = 0; l < 256; ++l)
754 for (r = 0; r < 256; ++r)
757 ul = qFT_Get_Char_Index(font->face, l);
758 ur = qFT_Get_Char_Index(font->face, r);
759 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
761 fmap->kerning.kerning[l][r][0] = 0;
762 fmap->kerning.kerning[l][r][1] = 0;
766 fmap->kerning.kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
767 fmap->kerning.kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
775 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
780 int matchsize = -10000;
782 float fsize_x, fsize_y;
783 ft2_font_map_t **maps = font->font_maps;
785 fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value;
787 fsize_x = *outw * vid.width / vid_conwidth.value;
789 fsize_y = *outh * vid.height / vid_conheight.value;
794 fsize_x = fsize_y = 16;
804 for (m = 0; m < MAX_FONT_SIZES; ++m)
808 // "round up" to the bigger size if two equally-valued matches exist
809 nval = 0.5 * (abs(maps[m]->size - fsize_x) + abs(maps[m]->size - fsize_y));
810 if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
814 matchsize = maps[m]->size;
815 if (value == 0) // there is no better match
819 if (value <= r_font_size_snapping.value)
821 // do NOT keep the aspect for perfect rendering
822 if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
823 if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width;
828 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
830 if (index < 0 || index >= MAX_FONT_SIZES)
832 return font->font_maps[index];
835 static qboolean Font_SetSize(ft2_font_t *font, float w, float h)
837 if (font->currenth == h &&
838 ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
839 font->currentw == w)) // same size has been requested
843 // sorry, but freetype doesn't seem to care about other sizes
846 if (font->image_font)
848 if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
853 if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
861 qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
863 ft2_font_map_t *fmap;
864 if (!font->has_kerning || !r_font_kerning.integer)
866 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
868 fmap = font->font_maps[map_index];
871 if (left < 256 && right < 256)
873 //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
874 // quick-kerning, be aware of the size: scale it
875 if (outx) *outx = fmap->kerning.kerning[left][right][0];// * (w / (float)fmap->size);
876 if (outy) *outy = fmap->kerning.kerning[left][right][1];// * (h / (float)fmap->size);
884 //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
886 if (!Font_SetSize(font, w, h))
888 // this deserves an error message
889 Con_Printf("Failed to get kerning for %s\n", font->name);
892 ul = qFT_Get_Char_Index(font->face, left);
893 ur = qFT_Get_Char_Index(font->face, right);
894 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
896 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);
897 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);
901 if (!Font_SetSize(font, fmap->intSize, fmap->intSize))
903 // this deserves an error message
904 Con_Printf("Failed to get kerning for %s\n", font->name);
907 ul = qFT_Get_Char_Index(font->face, left);
908 ur = qFT_Get_Char_Index(font->face, right);
909 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
911 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);// * (w / (float)fmap->size);
912 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);// * (h / (float)fmap->size);
919 qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
921 return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
924 static void UnloadMapRec(ft2_font_map_t *map)
928 R_FreeTexture(map->texture);
932 UnloadMapRec(map->next);
936 void Font_UnloadFont(ft2_font_t *font)
939 if (font->attachments && font->attachmentcount)
941 Mem_Free(font->attachments);
942 font->attachmentcount = 0;
943 font->attachments = NULL;
945 for (i = 0; i < MAX_FONT_SIZES; ++i)
947 if (font->font_maps[i])
949 UnloadMapRec(font->font_maps[i]);
950 font->font_maps[i] = NULL;
957 qFT_Done_Face((FT_Face)font->face);
963 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size)
965 float intSize = size;
968 if (!Font_SetSize(font, intSize, intSize))
970 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, size, intSize);
973 if ((fontface->size->metrics.height>>6) <= size)
977 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, size);
984 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
986 char map_identifier[MAX_QPATH];
987 unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
993 int gpad_l, gpad_r, gpad_t, gpad_b;
996 int gR, gC; // glyph position: row and column
998 ft2_font_map_t *map, *next;
1003 int bytesPerPixel = 4; // change the conversion loop too if you change this!
1008 if (r_font_use_alpha_textures.integer)
1011 if (font->image_font)
1012 fontface = (FT_Face)font->next->face;
1014 fontface = (FT_Face)font->face;
1016 switch(font->settings->antialias)
1019 switch(font->settings->hinting)
1022 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1026 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1030 load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1036 switch(font->settings->hinting)
1039 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1042 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
1045 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1049 load_flags = FT_LOAD_TARGET_NORMAL;
1055 //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
1057 if (font->image_font && mapstart->intSize < 0)
1058 mapstart->intSize = mapstart->size;
1059 if (mapstart->intSize < 0)
1062 mapstart->intSize = mapstart->size;
1065 if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1067 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
1070 if ((fontface->size->metrics.height>>6) <= mapstart->size)
1072 if (mapstart->intSize < 2)
1074 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
1077 --mapstart->intSize;
1080 if ((mapstart->intSize = Font_SearchSize(font, fontface, mapstart->size)) <= 0)
1082 Con_DPrintf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
1085 if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1087 Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
1091 map = Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
1094 Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
1098 Font_Postprocess(font, NULL, 0, bytesPerPixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
1100 // copy over the information
1101 map->size = mapstart->size;
1102 map->intSize = mapstart->intSize;
1103 map->glyphSize = mapstart->glyphSize;
1104 map->sfx = mapstart->sfx;
1105 map->sfy = mapstart->sfy;
1107 pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
1108 data = Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
1111 Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
1115 memset(map->width_of, 0, sizeof(map->width_of));
1117 // initialize as white texture with zero alpha
1119 while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
1121 if (bytesPerPixel == 4)
1131 map->start = mapidx * FONT_CHARS_PER_MAP;
1133 while(next->next && next->next->start < map->start)
1135 map->next = next->next;
1140 for (ch = map->start;
1141 ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
1144 FT_ULong glyphIndex;
1148 unsigned char *imagedata, *dst, *src;
1149 glyph_slot_t *mapglyph;
1151 int pad_l, pad_r, pad_t, pad_b;
1153 mapch = ch - map->start;
1155 if (developer_font.integer)
1156 Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
1159 if (gC >= FONT_CHARS_PER_LINE)
1161 gC -= FONT_CHARS_PER_LINE;
1165 imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
1166 imagedata += gpad_t * pitch + gpad_l * bytesPerPixel;
1167 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
1168 // we need the glyphIndex
1171 if (font->image_font && mapch == ch && img_fontmap[mapch])
1173 map->glyphs[mapch].image = true;
1176 glyphIndex = qFT_Get_Char_Index(face, ch);
1177 if (glyphIndex == 0)
1179 // by convention, 0 is the "missing-glyph"-glyph
1180 // try to load from a fallback font
1181 for(usefont = font->next; usefont != NULL; usefont = usefont->next)
1183 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
1186 face = usefont->face;
1187 glyphIndex = qFT_Get_Char_Index(face, ch);
1188 if (glyphIndex == 0)
1190 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1197 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1198 // now we let it use the "missing-glyph"-glyph
1208 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1211 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1212 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1217 glyph = face->glyph;
1218 bmp = &glyph->bitmap;
1223 if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
1224 Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
1225 if (w > map->glyphSize)
1226 w = map->glyphSize - gpad_l - gpad_r;
1227 if (h > map->glyphSize)
1231 switch (bmp->pixel_mode)
1233 case FT_PIXEL_MODE_MONO:
1234 if (developer_font.integer)
1235 Con_DPrint("glyphinfo: Pixel Mode: MONO\n");
1237 case FT_PIXEL_MODE_GRAY2:
1238 if (developer_font.integer)
1239 Con_DPrint("glyphinfo: Pixel Mode: GRAY2\n");
1241 case FT_PIXEL_MODE_GRAY4:
1242 if (developer_font.integer)
1243 Con_DPrint("glyphinfo: Pixel Mode: GRAY4\n");
1245 case FT_PIXEL_MODE_GRAY:
1246 if (developer_font.integer)
1247 Con_DPrint("glyphinfo: Pixel Mode: GRAY\n");
1250 if (developer_font.integer)
1251 Con_DPrintf("glyphinfo: Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1253 Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1256 for (y = 0; y < h; ++y)
1258 dst = imagedata + y * pitch;
1259 src = bmp->buffer + y * bmp->pitch;
1261 switch (bmp->pixel_mode)
1263 case FT_PIXEL_MODE_MONO:
1264 dst += bytesPerPixel - 1; // shift to alpha byte
1265 for (x = 0; x < bmp->width; x += 8)
1267 unsigned char ch = *src++;
1268 *dst = 255 * !!((ch & 0x80) >> 7); dst += bytesPerPixel;
1269 *dst = 255 * !!((ch & 0x40) >> 6); dst += bytesPerPixel;
1270 *dst = 255 * !!((ch & 0x20) >> 5); dst += bytesPerPixel;
1271 *dst = 255 * !!((ch & 0x10) >> 4); dst += bytesPerPixel;
1272 *dst = 255 * !!((ch & 0x08) >> 3); dst += bytesPerPixel;
1273 *dst = 255 * !!((ch & 0x04) >> 2); dst += bytesPerPixel;
1274 *dst = 255 * !!((ch & 0x02) >> 1); dst += bytesPerPixel;
1275 *dst = 255 * !!((ch & 0x01) >> 0); dst += bytesPerPixel;
1278 case FT_PIXEL_MODE_GRAY2:
1279 dst += bytesPerPixel - 1; // shift to alpha byte
1280 for (x = 0; x < bmp->width; x += 4)
1282 unsigned char ch = *src++;
1283 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
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;
1289 case FT_PIXEL_MODE_GRAY4:
1290 dst += bytesPerPixel - 1; // shift to alpha byte
1291 for (x = 0; x < bmp->width; x += 2)
1293 unsigned char ch = *src++;
1294 *dst = ( ((ch & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
1295 *dst = ( ((ch & 0x0F) ) * 0x11); dst += bytesPerPixel;
1298 case FT_PIXEL_MODE_GRAY:
1299 // in this case pitch should equal width
1300 for (tp = 0; tp < bmp->pitch; ++tp)
1301 dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
1303 //memcpy((void*)dst, (void*)src, bmp->pitch);
1304 //dst += bmp->pitch;
1315 Font_Postprocess(font, imagedata, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1317 // now fill map->glyphs[ch - map->start]
1318 mapglyph = &map->glyphs[mapch];
1322 // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1324 double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1325 //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1326 double advance = (glyph->advance.x / 64.0) / map->size;
1327 //double mWidth = (glyph->metrics.width >> 6) / map->size;
1328 //double mHeight = (glyph->metrics.height >> 6) / map->size;
1330 mapglyph->txmin = ( (double)(gC * map->glyphSize) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1331 mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1332 mapglyph->tymin = ( (double)(gR * map->glyphSize) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1333 mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1334 //mapglyph->vxmin = bearingX;
1335 //mapglyph->vxmax = bearingX + mWidth;
1336 mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
1337 mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
1338 //mapglyph->vymin = -bearingY;
1339 //mapglyph->vymax = mHeight - bearingY;
1340 mapglyph->vymin = (-glyph->bitmap_top - pad_t) / map->size;
1341 mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
1342 //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);
1343 //mapglyph->advance_x = advance * usefont->size;
1344 //mapglyph->advance_x = advance;
1345 mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1346 mapglyph->advance_y = 0;
1348 if (developer_font.integer)
1350 Con_DPrintf("glyphinfo: Glyph: %lu at (%i, %i)\n", (unsigned long)ch, gC, gR);
1351 Con_DPrintf("glyphinfo: %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1352 if (ch >= 32 && ch <= 128)
1353 Con_DPrintf("glyphinfo: Character: %c\n", (int)ch);
1354 Con_DPrintf("glyphinfo: Vertex info:\n");
1355 Con_DPrintf("glyphinfo: X: ( %f -- %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1356 Con_DPrintf("glyphinfo: Y: ( %f -- %f )\n", mapglyph->vymin, mapglyph->vymax);
1357 Con_DPrintf("glyphinfo: Texture info:\n");
1358 Con_DPrintf("glyphinfo: S: ( %f -- %f )\n", mapglyph->txmin, mapglyph->txmax);
1359 Con_DPrintf("glyphinfo: T: ( %f -- %f )\n", mapglyph->tymin, mapglyph->tymax);
1360 Con_DPrintf("glyphinfo: Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1363 map->glyphs[mapch].image = false;
1366 // create a texture from the data now
1368 if (developer_font.integer > 100)
1370 // LordHavoc: why are we writing this? And why not write it as TGA using the appropriate function?
1371 // view using `display -depth 8 -size 512x512 name_page.rgba` (be sure to use a correct -size parameter)
1372 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u.rgba", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1373 FS_WriteFile(map_identifier, data, pitch * FONT_CHAR_LINES * map->glyphSize);
1375 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1377 // probably use bytesPerPixel here instead?
1378 if (r_font_use_alpha_textures.integer)
1380 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1381 map->glyphSize * FONT_CHARS_PER_LINE,
1382 map->glyphSize * FONT_CHAR_LINES,
1383 data, TEXTYPE_ALPHA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL);
1385 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1386 map->glyphSize * FONT_CHARS_PER_LINE,
1387 map->glyphSize * FONT_CHAR_LINES,
1388 data, TEXTYPE_RGBA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL);
1394 // if the first try isn't successful, keep it with a broken texture
1395 // otherwise we retry to load it every single frame where ft2 rendering is used
1396 // this would be bad...
1397 // only `data' must be freed
1398 Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
1399 font->name, mapstart->size, mapidx);
1407 qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
1409 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1411 // the first map must have been loaded already
1412 if (!font->font_maps[map_index])
1414 return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
1417 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1419 while (start && start->start + FONT_CHARS_PER_MAP <= ch)
1420 start = start->next;
1421 if (start && start->start > ch)