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)
321 //vw = ((vid.width > 0) ? vid.width : vid_width.value);
322 vh = ((vid.height > 0) ? vid.height : vid_height.value);
323 // now try to scale to our actual size:
324 sn = sz * vh / vid_conheight.value;
326 if ( sn - (float)si >= 0.5 )
331 float Font_SnapTo(float val, float snapwidth)
333 return floor(val / snapwidth + 0.5f) * snapwidth;
336 static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font);
337 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only);
338 qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt)
341 ft2_font_t *ft2, *fbfont, *fb;
350 // check if a fallback font has been specified, if it has been, and the
351 // font fails to load, use the image font as main font
352 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
354 if (dpfnt->fallbacks[i][0])
358 if (!Font_LoadFile(name, dpfnt->req_face, &dpfnt->settings, ft2))
360 if (i >= MAX_FONT_FALLBACKS)
366 strlcpy(ft2->name, name, sizeof(ft2->name));
367 ft2->image_font = true;
368 ft2->has_kerning = false;
372 ft2->image_font = false;
375 // attempt to load fallback fonts:
377 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
379 if (!dpfnt->fallbacks[i][0])
381 if (! (fb = Font_Alloc()) )
383 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
387 if (!Font_LoadFile(dpfnt->fallbacks[i], dpfnt->fallback_faces[i], &dpfnt->settings, fb))
389 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
394 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
396 if (Font_LoadSize(fb, Font_VirtualToRealSize(dpfnt->req_sizes[s]), true))
401 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
406 // at least one size of the fallback font loaded successfully
412 if (fbfont == ft2 && ft2->image_font)
414 // no fallbacks were loaded successfully:
421 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
423 if (Font_LoadSize(ft2, Font_VirtualToRealSize(dpfnt->req_sizes[s]), false))
428 // loading failed for every requested size
429 Font_UnloadFont(ft2);
435 //Con_Printf("%i sizes loaded\n", count);
440 static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font)
443 char filename[MAX_QPATH];
447 fs_offset_t datasize;
449 memset(font, 0, sizeof(*font));
451 if (!Font_OpenLibrary())
453 if (!r_font_disable_freetype.integer)
455 Con_Printf("WARNING: can't open load font %s\n"
456 "You need the FreeType2 DLL to load font files\n",
462 font->settings = settings;
464 namelen = strlen(name);
466 memcpy(filename, name, namelen);
467 memcpy(filename + namelen, ".ttf", 5);
468 data = FS_LoadFile(filename, font_mempool, false, &datasize);
471 memcpy(filename + namelen, ".otf", 5);
472 data = FS_LoadFile(filename, font_mempool, false, &datasize);
476 ft2_attachment_t afm;
478 memcpy(filename + namelen, ".pfb", 5);
479 data = FS_LoadFile(filename, font_mempool, false, &datasize);
483 memcpy(filename + namelen, ".afm", 5);
484 afm.data = FS_LoadFile(filename, font_mempool, false, &afm.size);
487 Font_Attach(font, &afm);
493 // FS_LoadFile being not-quiet should print an error :)
496 Con_Printf("Loading font %s face %i...\n", filename, _face);
498 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
499 if (status && _face != 0)
501 Con_Printf("Failed to load face %i of %s. Falling back to face 0\n", _face, name);
503 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, 0, (FT_Face*)&font->face);
507 Con_Printf("ERROR: can't create face for %s\n"
508 "Error %i\n", // TODO: error strings
510 Font_UnloadFont(font);
514 // add the attachments
515 for (i = 0; i < font->attachmentcount; ++i)
518 memset(&args, 0, sizeof(args));
519 args.flags = FT_OPEN_MEMORY;
520 args.memory_base = (const FT_Byte*)font->attachments[i].data;
521 args.memory_size = font->attachments[i].size;
522 if (qFT_Attach_Stream(font->face, &args))
523 Con_Printf("Failed to add attachment %u to %s\n", (unsigned)i, font->name);
526 memcpy(font->name, name, namelen+1);
527 font->image_font = false;
528 font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
532 void Font_Postprocess_Update(ft2_font_t *fnt, int bpp, int w, int h)
535 float gausstable[2*POSTPROCESS_MAXRADIUS+1];
536 qboolean need_gauss = (!pp.buf || pp.blur != fnt->settings->blur || pp.shadowz != fnt->settings->shadowz);
537 qboolean need_circle = (!pp.buf || pp.outline != fnt->settings->outline || pp.shadowx != fnt->settings->shadowx || pp.shadowy != fnt->settings->shadowy);
538 pp.blur = fnt->settings->blur;
539 pp.outline = fnt->settings->outline;
540 pp.shadowx = fnt->settings->shadowx;
541 pp.shadowy = fnt->settings->shadowy;
542 pp.shadowz = fnt->settings->shadowz;
543 pp.outlinepadding_l = bound(0, ceil(pp.outline - pp.shadowx), POSTPROCESS_MAXRADIUS);
544 pp.outlinepadding_r = bound(0, ceil(pp.outline + pp.shadowx), POSTPROCESS_MAXRADIUS);
545 pp.outlinepadding_t = bound(0, ceil(pp.outline - pp.shadowy), POSTPROCESS_MAXRADIUS);
546 pp.outlinepadding_b = bound(0, ceil(pp.outline + pp.shadowy), POSTPROCESS_MAXRADIUS);
547 pp.blurpadding_lt = bound(0, ceil(pp.blur - pp.shadowz), POSTPROCESS_MAXRADIUS);
548 pp.blurpadding_rb = bound(0, ceil(pp.blur + pp.shadowz), POSTPROCESS_MAXRADIUS);
549 pp.padding_l = pp.blurpadding_lt + pp.outlinepadding_l;
550 pp.padding_r = pp.blurpadding_rb + pp.outlinepadding_r;
551 pp.padding_t = pp.blurpadding_lt + pp.outlinepadding_t;
552 pp.padding_b = pp.blurpadding_rb + pp.outlinepadding_b;
556 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
557 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));
558 for(x = -pp.blurpadding_rb; x <= pp.blurpadding_lt; ++x)
559 sum += gausstable[POSTPROCESS_MAXRADIUS+x];
560 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
561 pp.gausstable[POSTPROCESS_MAXRADIUS+x] = floor(gausstable[POSTPROCESS_MAXRADIUS+x] / sum * 255 + 0.5);
565 for(y = -POSTPROCESS_MAXRADIUS; y <= POSTPROCESS_MAXRADIUS; ++y)
566 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
568 float d = pp.outline + 1 - sqrt(pow(x + pp.shadowx, 2) + pow(y + pp.shadowy, 2));
569 pp.circlematrix[POSTPROCESS_MAXRADIUS+y][POSTPROCESS_MAXRADIUS+x] = (d >= 1) ? 255 : (d <= 0) ? 0 : floor(d * 255 + 0.5);
572 pp.bufwidth = w + pp.padding_l + pp.padding_r;
573 pp.bufheight = h + pp.padding_t + pp.padding_b;
574 pp.bufpitch = pp.bufwidth;
575 needed = pp.bufwidth * pp.bufheight;
576 if(!pp.buf || pp.bufsize < needed * 2)
580 pp.bufsize = needed * 4;
581 pp.buf = Mem_Alloc(font_mempool, pp.bufsize);
582 pp.buf2 = pp.buf + needed;
586 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)
589 Font_Postprocess_Update(fnt, bpp, w, h);
594 // perform operation, not exceeding the passed padding values,
595 // but possibly reducing them
596 *pad_l = min(*pad_l, pp.padding_l);
597 *pad_r = min(*pad_r, pp.padding_r);
598 *pad_t = min(*pad_t, pp.padding_t);
599 *pad_b = min(*pad_b, pp.padding_b);
601 // calculate gauss table
603 // outline the font (RGBA only)
604 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
606 // this is like mplayer subtitle rendering
607 // bbuffer, bitmap buffer: this is our font
608 // abuffer, alpha buffer: this is pp.buf
609 // tmp: this is pp.buf2
611 // create outline buffer
612 memset(pp.buf, 0, pp.bufwidth * pp.bufheight);
613 for(y = -*pad_t; y < h + *pad_b; ++y)
614 for(x = -*pad_l; x < w + *pad_r; ++x)
616 int x1 = max(-x, -pp.outlinepadding_r);
617 int y1 = max(-y, -pp.outlinepadding_b);
618 int x2 = min(pp.outlinepadding_l, w-1-x);
619 int y2 = min(pp.outlinepadding_t, h-1-y);
623 for(my = y1; my <= y2; ++my)
624 for(mx = x1; mx <= x2; ++mx)
626 cur = pp.circlematrix[POSTPROCESS_MAXRADIUS+my][POSTPROCESS_MAXRADIUS+mx] * (int)imagedata[(x+mx) * bpp + pitch * (y+my) + (bpp - 1)];
630 pp.buf[((x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t))] = (highest + 128) / 255;
633 // blur the outline buffer
634 if(pp.blur > 0 || pp.shadowz != 0)
637 for(y = 0; y < pp.bufheight; ++y)
638 for(x = 0; x < pp.bufwidth; ++x)
640 int x1 = max(-x, -pp.blurpadding_rb);
641 int x2 = min(pp.blurpadding_lt, pp.bufwidth-1-x);
644 for(mx = x1; mx <= x2; ++mx)
645 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+mx] * (int)pp.buf[(x+mx) + pp.bufpitch * y];
646 pp.buf2[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
650 for(y = 0; y < pp.bufheight; ++y)
651 for(x = 0; x < pp.bufwidth; ++x)
653 int y1 = max(-y, -pp.blurpadding_rb);
654 int y2 = min(pp.blurpadding_lt, pp.bufheight-1-y);
657 for(my = y1; my <= y2; ++my)
658 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+my] * (int)pp.buf2[x + pp.bufpitch * (y+my)];
659 pp.buf[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
663 // paste the outline below the font
664 for(y = -*pad_t; y < h + *pad_b; ++y)
665 for(x = -*pad_l; x < w + *pad_r; ++x)
667 unsigned char outlinealpha = pp.buf[(x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t)];
670 unsigned char oldalpha = imagedata[x * bpp + pitch * y + (bpp - 1)];
671 // a' = 1 - (1 - a1) (1 - a2)
672 unsigned char newalpha = 255 - ((255 - (int)outlinealpha) * (255 - (int)oldalpha)) / 255; // this is >= oldalpha
673 // c' = (a2 c2 - a1 a2 c1 + a1 c1) / a' = (a2 c2 + a1 (1 - a2) c1) / a'
674 unsigned char oldfactor = (255 * (int)oldalpha) / newalpha;
675 //unsigned char outlinefactor = ((255 - oldalpha) * (int)outlinealpha) / newalpha;
677 for(i = 0; i < bpp-1; ++i)
679 unsigned char c = imagedata[x * bpp + pitch * y + i];
680 c = (c * (int)oldfactor) / 255 /* + outlinecolor[i] * (int)outlinefactor */;
681 imagedata[x * bpp + pitch * y + i] = c;
683 imagedata[x * bpp + pitch * y + (bpp - 1)] = newalpha;
685 //imagedata[x * bpp + pitch * y + (bpp - 1)] |= 0x80;
691 // just calculate parameters
692 *pad_l = pp.padding_l;
693 *pad_r = pp.padding_r;
694 *pad_t = pp.padding_t;
695 *pad_b = pp.padding_b;
699 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size);
700 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
701 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only)
704 ft2_font_map_t *fmap, temp;
705 int gpad_l, gpad_r, gpad_t, gpad_b;
707 if (!(size > 0.001f && size < 1000.0f))
712 if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
715 for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
717 if (!font->font_maps[map_index])
719 // if a similar size has already been loaded, ignore this one
720 //abs(font->font_maps[map_index]->size - size) < 4
721 if (font->font_maps[map_index]->size == size)
725 if (map_index >= MAX_FONT_SIZES)
730 if (font->image_font)
731 fontface = (FT_Face)font->next->face;
733 fontface = (FT_Face)font->face;
734 return (Font_SearchSize(font, fontface, size) > 0);
737 Font_Postprocess(font, NULL, 0, 4, size*2, size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
739 memset(&temp, 0, sizeof(temp));
741 temp.glyphSize = CeilPowerOf2(size*2 + max(gpad_l + gpad_r, gpad_t + gpad_b));
742 temp.sfx = (1.0/64.0)/(double)size;
743 temp.sfy = (1.0/64.0)/(double)size;
744 temp.intSize = -1; // negative value: LoadMap must search now :)
745 if (!Font_LoadMap(font, &temp, 0, &fmap))
747 Con_Printf("ERROR: can't load the first character map for %s\n"
750 Font_UnloadFont(font);
753 font->font_maps[map_index] = temp.next;
755 fmap->sfx = temp.sfx;
756 fmap->sfy = temp.sfy;
758 // load the default kerning vector:
759 if (font->has_kerning)
763 for (l = 0; l < 256; ++l)
765 for (r = 0; r < 256; ++r)
768 ul = qFT_Get_Char_Index(font->face, l);
769 ur = qFT_Get_Char_Index(font->face, r);
770 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
772 fmap->kerning.kerning[l][r][0] = 0;
773 fmap->kerning.kerning[l][r][1] = 0;
777 fmap->kerning.kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
778 fmap->kerning.kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
786 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
791 int matchsize = -10000;
793 float fsize_x, fsize_y;
794 ft2_font_map_t **maps = font->font_maps;
796 fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value;
798 fsize_x = *outw * vid.width / vid_conwidth.value;
800 fsize_y = *outh * vid.height / vid_conheight.value;
805 fsize_x = fsize_y = 16;
815 for (m = 0; m < MAX_FONT_SIZES; ++m)
819 // "round up" to the bigger size if two equally-valued matches exist
820 nval = 0.5 * (abs(maps[m]->size - fsize_x) + abs(maps[m]->size - fsize_y));
821 if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
825 matchsize = maps[m]->size;
826 if (value == 0) // there is no better match
830 if (value <= r_font_size_snapping.value)
832 // do NOT keep the aspect for perfect rendering
833 if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
834 if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width;
839 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
841 if (index < 0 || index >= MAX_FONT_SIZES)
843 return font->font_maps[index];
846 static qboolean Font_SetSize(ft2_font_t *font, float w, float h)
848 if (font->currenth == h &&
849 ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
850 font->currentw == w)) // same size has been requested
854 // sorry, but freetype doesn't seem to care about other sizes
857 if (font->image_font)
859 if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
864 if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
872 qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
874 ft2_font_map_t *fmap;
875 if (!font->has_kerning || !r_font_kerning.integer)
877 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
879 fmap = font->font_maps[map_index];
882 if (left < 256 && right < 256)
884 //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
885 // quick-kerning, be aware of the size: scale it
886 if (outx) *outx = fmap->kerning.kerning[left][right][0];// * (w / (float)fmap->size);
887 if (outy) *outy = fmap->kerning.kerning[left][right][1];// * (h / (float)fmap->size);
895 //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
897 if (!Font_SetSize(font, w, h))
899 // this deserves an error message
900 Con_Printf("Failed to get kerning for %s\n", font->name);
903 ul = qFT_Get_Char_Index(font->face, left);
904 ur = qFT_Get_Char_Index(font->face, right);
905 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
907 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);
908 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);
912 if (!Font_SetSize(font, fmap->intSize, fmap->intSize))
914 // this deserves an error message
915 Con_Printf("Failed to get kerning for %s\n", font->name);
918 ul = qFT_Get_Char_Index(font->face, left);
919 ur = qFT_Get_Char_Index(font->face, right);
920 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
922 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);// * (w / (float)fmap->size);
923 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);// * (h / (float)fmap->size);
930 qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
932 return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
935 static void UnloadMapRec(ft2_font_map_t *map)
939 R_FreeTexture(map->texture);
943 UnloadMapRec(map->next);
947 void Font_UnloadFont(ft2_font_t *font)
950 if (font->attachments && font->attachmentcount)
952 Mem_Free(font->attachments);
953 font->attachmentcount = 0;
954 font->attachments = NULL;
956 for (i = 0; i < MAX_FONT_SIZES; ++i)
958 if (font->font_maps[i])
960 UnloadMapRec(font->font_maps[i]);
961 font->font_maps[i] = NULL;
968 qFT_Done_Face((FT_Face)font->face);
974 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size)
976 float intSize = size;
979 if (!Font_SetSize(font, intSize, intSize))
981 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, size, intSize);
984 if ((fontface->size->metrics.height>>6) <= size)
988 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, size);
995 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
997 char map_identifier[MAX_QPATH];
998 unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
1003 FT_Int32 load_flags;
1004 int gpad_l, gpad_r, gpad_t, gpad_b;
1007 int gR, gC; // glyph position: row and column
1009 ft2_font_map_t *map, *next;
1010 ft2_font_t *usefont;
1014 int bytesPerPixel = 4; // change the conversion loop too if you change this!
1019 if (r_font_use_alpha_textures.integer)
1022 if (font->image_font)
1023 fontface = (FT_Face)font->next->face;
1025 fontface = (FT_Face)font->face;
1027 switch(font->settings->antialias)
1030 switch(font->settings->hinting)
1033 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1037 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1041 load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1047 switch(font->settings->hinting)
1050 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1053 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
1056 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1060 load_flags = FT_LOAD_TARGET_NORMAL;
1066 //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
1068 if (font->image_font && mapstart->intSize < 0)
1069 mapstart->intSize = mapstart->size;
1070 if (mapstart->intSize < 0)
1073 mapstart->intSize = mapstart->size;
1076 if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1078 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
1081 if ((fontface->size->metrics.height>>6) <= mapstart->size)
1083 if (mapstart->intSize < 2)
1085 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
1088 --mapstart->intSize;
1091 if ((mapstart->intSize = Font_SearchSize(font, fontface, mapstart->size)) <= 0)
1093 Con_DPrintf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
1096 if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1098 Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
1102 map = Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
1105 Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
1109 Font_Postprocess(font, NULL, 0, bytesPerPixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
1111 // copy over the information
1112 map->size = mapstart->size;
1113 map->intSize = mapstart->intSize;
1114 map->glyphSize = mapstart->glyphSize;
1115 map->sfx = mapstart->sfx;
1116 map->sfy = mapstart->sfy;
1118 pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
1119 data = Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
1122 Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
1126 memset(map->width_of, 0, sizeof(map->width_of));
1128 // initialize as white texture with zero alpha
1130 while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
1132 if (bytesPerPixel == 4)
1142 map->start = mapidx * FONT_CHARS_PER_MAP;
1144 while(next->next && next->next->start < map->start)
1146 map->next = next->next;
1151 for (ch = map->start;
1152 ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
1155 FT_ULong glyphIndex;
1159 unsigned char *imagedata, *dst, *src;
1160 glyph_slot_t *mapglyph;
1162 int pad_l, pad_r, pad_t, pad_b;
1164 mapch = ch - map->start;
1166 if (developer_font.integer)
1167 Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
1170 if (gC >= FONT_CHARS_PER_LINE)
1172 gC -= FONT_CHARS_PER_LINE;
1176 imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
1177 imagedata += gpad_t * pitch + gpad_l * bytesPerPixel;
1178 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
1179 // we need the glyphIndex
1182 if (font->image_font && mapch == ch && img_fontmap[mapch])
1184 map->glyphs[mapch].image = true;
1187 glyphIndex = qFT_Get_Char_Index(face, ch);
1188 if (glyphIndex == 0)
1190 // by convention, 0 is the "missing-glyph"-glyph
1191 // try to load from a fallback font
1192 for(usefont = font->next; usefont != NULL; usefont = usefont->next)
1194 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
1197 face = usefont->face;
1198 glyphIndex = qFT_Get_Char_Index(face, ch);
1199 if (glyphIndex == 0)
1201 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1208 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1209 // now we let it use the "missing-glyph"-glyph
1219 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1222 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1223 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1228 glyph = face->glyph;
1229 bmp = &glyph->bitmap;
1234 if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
1235 Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
1236 if (w > map->glyphSize)
1237 w = map->glyphSize - gpad_l - gpad_r;
1238 if (h > map->glyphSize)
1242 switch (bmp->pixel_mode)
1244 case FT_PIXEL_MODE_MONO:
1245 if (developer_font.integer)
1246 Con_DPrint("glyphinfo: Pixel Mode: MONO\n");
1248 case FT_PIXEL_MODE_GRAY2:
1249 if (developer_font.integer)
1250 Con_DPrint("glyphinfo: Pixel Mode: GRAY2\n");
1252 case FT_PIXEL_MODE_GRAY4:
1253 if (developer_font.integer)
1254 Con_DPrint("glyphinfo: Pixel Mode: GRAY4\n");
1256 case FT_PIXEL_MODE_GRAY:
1257 if (developer_font.integer)
1258 Con_DPrint("glyphinfo: Pixel Mode: GRAY\n");
1261 if (developer_font.integer)
1262 Con_DPrintf("glyphinfo: Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1264 Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1267 for (y = 0; y < h; ++y)
1269 dst = imagedata + y * pitch;
1270 src = bmp->buffer + y * bmp->pitch;
1272 switch (bmp->pixel_mode)
1274 case FT_PIXEL_MODE_MONO:
1275 dst += bytesPerPixel - 1; // shift to alpha byte
1276 for (x = 0; x < bmp->width; x += 8)
1278 unsigned char ch = *src++;
1279 *dst = 255 * !!((ch & 0x80) >> 7); dst += bytesPerPixel;
1280 *dst = 255 * !!((ch & 0x40) >> 6); dst += bytesPerPixel;
1281 *dst = 255 * !!((ch & 0x20) >> 5); dst += bytesPerPixel;
1282 *dst = 255 * !!((ch & 0x10) >> 4); dst += bytesPerPixel;
1283 *dst = 255 * !!((ch & 0x08) >> 3); dst += bytesPerPixel;
1284 *dst = 255 * !!((ch & 0x04) >> 2); dst += bytesPerPixel;
1285 *dst = 255 * !!((ch & 0x02) >> 1); dst += bytesPerPixel;
1286 *dst = 255 * !!((ch & 0x01) >> 0); dst += bytesPerPixel;
1289 case FT_PIXEL_MODE_GRAY2:
1290 dst += bytesPerPixel - 1; // shift to alpha byte
1291 for (x = 0; x < bmp->width; x += 4)
1293 unsigned char ch = *src++;
1294 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
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;
1300 case FT_PIXEL_MODE_GRAY4:
1301 dst += bytesPerPixel - 1; // shift to alpha byte
1302 for (x = 0; x < bmp->width; x += 2)
1304 unsigned char ch = *src++;
1305 *dst = ( ((ch & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
1306 *dst = ( ((ch & 0x0F) ) * 0x11); dst += bytesPerPixel;
1309 case FT_PIXEL_MODE_GRAY:
1310 // in this case pitch should equal width
1311 for (tp = 0; tp < bmp->pitch; ++tp)
1312 dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
1314 //memcpy((void*)dst, (void*)src, bmp->pitch);
1315 //dst += bmp->pitch;
1326 Font_Postprocess(font, imagedata, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1328 // now fill map->glyphs[ch - map->start]
1329 mapglyph = &map->glyphs[mapch];
1333 // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1335 double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1336 //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1337 double advance = (glyph->advance.x / 64.0) / map->size;
1338 //double mWidth = (glyph->metrics.width >> 6) / map->size;
1339 //double mHeight = (glyph->metrics.height >> 6) / map->size;
1341 mapglyph->txmin = ( (double)(gC * map->glyphSize) + (double)(gpad_l - pad_l) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1342 mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1343 mapglyph->tymin = ( (double)(gR * map->glyphSize) + (double)(gpad_r - pad_r) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1344 mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1345 //mapglyph->vxmin = bearingX;
1346 //mapglyph->vxmax = bearingX + mWidth;
1347 mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
1348 mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
1349 //mapglyph->vymin = -bearingY;
1350 //mapglyph->vymax = mHeight - bearingY;
1351 mapglyph->vymin = (-glyph->bitmap_top - pad_t) / map->size;
1352 mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
1353 //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);
1354 //mapglyph->advance_x = advance * usefont->size;
1355 //mapglyph->advance_x = advance;
1356 mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1357 mapglyph->advance_y = 0;
1359 if (developer_font.integer)
1361 Con_DPrintf("glyphinfo: Glyph: %lu at (%i, %i)\n", (unsigned long)ch, gC, gR);
1362 Con_DPrintf("glyphinfo: %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1363 if (ch >= 32 && ch <= 128)
1364 Con_DPrintf("glyphinfo: Character: %c\n", (int)ch);
1365 Con_DPrintf("glyphinfo: Vertex info:\n");
1366 Con_DPrintf("glyphinfo: X: ( %f -- %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1367 Con_DPrintf("glyphinfo: Y: ( %f -- %f )\n", mapglyph->vymin, mapglyph->vymax);
1368 Con_DPrintf("glyphinfo: Texture info:\n");
1369 Con_DPrintf("glyphinfo: S: ( %f -- %f )\n", mapglyph->txmin, mapglyph->txmax);
1370 Con_DPrintf("glyphinfo: T: ( %f -- %f )\n", mapglyph->tymin, mapglyph->tymax);
1371 Con_DPrintf("glyphinfo: Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1374 map->glyphs[mapch].image = false;
1377 // create a texture from the data now
1379 if (developer_font.integer > 100)
1381 // LordHavoc: why are we writing this? And why not write it as TGA using the appropriate function?
1382 // view using `display -depth 8 -size 512x512 name_page.rgba` (be sure to use a correct -size parameter)
1383 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u.rgba", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1384 FS_WriteFile(map_identifier, data, pitch * FONT_CHAR_LINES * map->glyphSize);
1386 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1388 // probably use bytesPerPixel here instead?
1389 if (r_font_use_alpha_textures.integer)
1391 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1392 map->glyphSize * FONT_CHARS_PER_LINE,
1393 map->glyphSize * FONT_CHAR_LINES,
1394 data, TEXTYPE_ALPHA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL);
1396 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1397 map->glyphSize * FONT_CHARS_PER_LINE,
1398 map->glyphSize * FONT_CHAR_LINES,
1399 data, TEXTYPE_RGBA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL);
1405 // if the first try isn't successful, keep it with a broken texture
1406 // otherwise we retry to load it every single frame where ft2 rendering is used
1407 // this would be bad...
1408 // only `data' must be freed
1409 Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
1410 font->name, mapstart->size, mapidx);
1418 qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
1420 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1422 // the first map must have been loaded already
1423 if (!font->font_maps[map_index])
1425 return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
1428 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1430 while (start && start->start + FONT_CHARS_PER_MAP <= ch)
1431 start = start->next;
1432 if (start && start->start > ch)