1 /* FreeType 2 and UTF-8 encoding support for
8 #include "ft2_fontdefs.h"
11 static int img_fontmap[256] = {
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,
14 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // shift+digit line
15 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // digits
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, // caps
18 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small
19 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small
20 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // specials
21 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // faces
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,
27 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
31 ================================================================================
32 CVars introduced with the freetype extension
33 ================================================================================
36 cvar_t r_font_disable_freetype = {CVAR_SAVE, "r_font_disable_freetype", "1", "disable freetype support for fonts entirely"};
37 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"};
38 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!"};
39 cvar_t r_font_kerning = {CVAR_SAVE, "r_font_kerning", "1", "Use kerning if available"};
40 cvar_t r_font_diskcache = {CVAR_SAVE, "r_font_diskcache", "0", "save font textures to disk for future loading rather than generating them every time"};
41 cvar_t r_font_compress = {CVAR_SAVE, "r_font_compress", "0", "use texture compression on font textures to save video memory"};
42 cvar_t r_font_nonpoweroftwo = {CVAR_SAVE, "r_font_nonpoweroftwo", "1", "use nonpoweroftwo textures for font (saves memory, potentially slower)"};
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;
142 /// FreeType library handle
143 static FT_Library font_ft2lib = NULL;
145 #define POSTPROCESS_MAXRADIUS 8
148 unsigned char *buf, *buf2;
149 int bufsize, bufwidth, bufheight, bufpitch;
150 float blur, outline, shadowx, shadowy, shadowz;
151 int padding_t, padding_b, padding_l, padding_r, blurpadding_lt, blurpadding_rb, outlinepadding_t, outlinepadding_b, outlinepadding_l, outlinepadding_r;
152 unsigned char circlematrix[2*POSTPROCESS_MAXRADIUS+1][2*POSTPROCESS_MAXRADIUS+1];
153 unsigned char gausstable[2*POSTPROCESS_MAXRADIUS+1];
156 static font_postprocess_t pp;
158 typedef struct fontfilecache_s
163 char path[MAX_QPATH];
166 #define MAX_FONTFILES 8
167 static fontfilecache_t fontfiles[MAX_FONTFILES];
168 static const unsigned char *fontfilecache_LoadFile(const char *path, qboolean quiet, fs_offset_t *filesizepointer)
173 for(i = 0; i < MAX_FONTFILES; ++i)
175 if(fontfiles[i].refcount > 0)
176 if(!strcmp(path, fontfiles[i].path))
178 *filesizepointer = fontfiles[i].len;
179 ++fontfiles[i].refcount;
180 return fontfiles[i].buf;
184 buf = FS_LoadFile(path, font_mempool, quiet, filesizepointer);
187 for(i = 0; i < MAX_FONTFILES; ++i)
188 if(fontfiles[i].refcount <= 0)
190 strlcpy(fontfiles[i].path, path, sizeof(fontfiles[i].path));
191 fontfiles[i].len = *filesizepointer;
192 fontfiles[i].buf = buf;
193 fontfiles[i].refcount = 1;
200 static void fontfilecache_Free(const unsigned char *buf)
203 for(i = 0; i < MAX_FONTFILES; ++i)
205 if(fontfiles[i].refcount > 0)
206 if(fontfiles[i].buf == buf)
208 if(--fontfiles[i].refcount <= 0)
210 Mem_Free(fontfiles[i].buf);
211 fontfiles[i].buf = NULL;
216 // if we get here, it used regular allocation
217 Mem_Free((void *) buf);
219 static void fontfilecache_FreeAll(void)
222 for(i = 0; i < MAX_FONTFILES; ++i)
224 if(fontfiles[i].refcount > 0)
225 Mem_Free(fontfiles[i].buf);
226 fontfiles[i].buf = NULL;
227 fontfiles[i].refcount = 0;
235 Unload the FreeType2 DLL
238 void Font_CloseLibrary (void)
240 fontfilecache_FreeAll();
242 Mem_FreePool(&font_mempool);
243 if (font_ft2lib && qFT_Done_FreeType)
245 qFT_Done_FreeType(font_ft2lib);
248 Sys_UnloadLibrary (&ft2_dll);
256 Try to load the FreeType2 DLL
259 qboolean Font_OpenLibrary (void)
261 const char* dllnames [] =
266 #elif defined(MACOSX)
267 "libfreetype.6.dylib",
276 if (r_font_disable_freetype.integer)
284 if (!Sys_LoadLibrary (dllnames, &ft2_dll, ft2funcs))
293 Initialize the freetype2 font subsystem
297 void font_start(void)
299 if (!Font_OpenLibrary())
302 if (qFT_Init_FreeType(&font_ft2lib))
304 Con_Print("ERROR: Failed to initialize the FreeType2 library!\n");
309 font_mempool = Mem_AllocPool("FONT", 0, NULL);
312 Con_Print("ERROR: Failed to allocate FONT memory pool!\n");
318 void font_shutdown(void)
321 for (i = 0; i < dp_fonts.maxsize; ++i)
323 if (dp_fonts.f[i].ft2)
325 Font_UnloadFont(dp_fonts.f[i].ft2);
326 dp_fonts.f[i].ft2 = NULL;
332 void font_newmap(void)
338 Cvar_RegisterVariable(&r_font_nonpoweroftwo);
339 Cvar_RegisterVariable(&r_font_disable_freetype);
340 Cvar_RegisterVariable(&r_font_use_alpha_textures);
341 Cvar_RegisterVariable(&r_font_size_snapping);
342 Cvar_RegisterVariable(&r_font_kerning);
343 Cvar_RegisterVariable(&r_font_diskcache);
344 Cvar_RegisterVariable(&r_font_compress);
345 Cvar_RegisterVariable(&developer_font);
347 // let's open it at startup already
352 ================================================================================
353 Implementation of a more or less lazy font loading and rendering code.
354 ================================================================================
357 #include "ft2_fontdefs.h"
359 ft2_font_t *Font_Alloc(void)
363 return (ft2_font_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_t));
366 static qboolean Font_Attach(ft2_font_t *font, ft2_attachment_t *attachment)
368 ft2_attachment_t *na;
370 font->attachmentcount++;
371 na = (ft2_attachment_t*)Mem_Alloc(font_mempool, sizeof(font->attachments[0]) * font->attachmentcount);
374 if (font->attachments && font->attachmentcount > 1)
376 memcpy(na, font->attachments, sizeof(font->attachments[0]) * (font->attachmentcount - 1));
377 Mem_Free(font->attachments);
379 memcpy(na + sizeof(font->attachments[0]) * (font->attachmentcount - 1), attachment, sizeof(*attachment));
380 font->attachments = na;
384 float Font_VirtualToRealSize(float sz)
392 //vw = ((vid.width > 0) ? vid.width : vid_width.value);
393 vh = ((vid.height > 0) ? vid.height : vid_height.value);
394 // now try to scale to our actual size:
395 sn = sz * vh / vid_conheight.value;
397 if ( sn - (float)si >= 0.5 )
402 float Font_SnapTo(float val, float snapwidth)
404 return floor(val / snapwidth + 0.5f) * snapwidth;
407 static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font);
408 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only);
409 qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt)
412 ft2_font_t *ft2, *fbfont, *fb;
422 // check if a fallback font has been specified, if it has been, and the
423 // font fails to load, use the image font as main font
424 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
426 if (dpfnt->fallbacks[i][0])
430 if (!Font_LoadFile(name, dpfnt->req_face, &dpfnt->settings, ft2))
432 if (i >= MAX_FONT_FALLBACKS)
438 strlcpy(ft2->name, name, sizeof(ft2->name));
439 ft2->image_font = true;
440 ft2->has_kerning = false;
444 ft2->image_font = false;
447 // attempt to load fallback fonts:
449 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
451 if (!dpfnt->fallbacks[i][0])
453 if (! (fb = Font_Alloc()) )
455 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
459 if (!Font_LoadFile(dpfnt->fallbacks[i], dpfnt->fallback_faces[i], &dpfnt->settings, fb))
461 if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.tga", dpfnt->fallbacks[i])))
462 if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.png", dpfnt->fallbacks[i])))
463 if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.jpg", dpfnt->fallbacks[i])))
464 if(!FS_FileExists(va(vabuf, sizeof(vabuf), "%s.pcx", dpfnt->fallbacks[i])))
465 Con_Printf("Failed to load font %s for fallback %i of font %s\n", dpfnt->fallbacks[i], i, name);
470 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
472 if (Font_LoadSize(fb, Font_VirtualToRealSize(dpfnt->req_sizes[s]), true))
477 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
482 // at least one size of the fallback font loaded successfully
488 if (fbfont == ft2 && ft2->image_font)
490 // no fallbacks were loaded successfully:
497 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
499 if (Font_LoadSize(ft2, Font_VirtualToRealSize(dpfnt->req_sizes[s]), false))
504 // loading failed for every requested size
505 Font_UnloadFont(ft2);
511 //Con_Printf("%i sizes loaded\n", count);
516 static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font)
519 char filename[MAX_QPATH];
522 const unsigned char *data;
523 fs_offset_t datasize;
525 memset(font, 0, sizeof(*font));
527 if (!Font_OpenLibrary())
529 if (!r_font_disable_freetype.integer)
531 Con_Printf("WARNING: can't open load font %s\n"
532 "You need the FreeType2 DLL to load font files\n",
538 font->settings = settings;
540 namelen = strlen(name);
542 // try load direct file
543 memcpy(filename, name, namelen+1);
544 data = fontfilecache_LoadFile(filename, false, &datasize);
548 memcpy(filename + namelen, ".ttf", 5);
549 data = fontfilecache_LoadFile(filename, false, &datasize);
554 memcpy(filename + namelen, ".otf", 5);
555 data = fontfilecache_LoadFile(filename, false, &datasize);
560 ft2_attachment_t afm;
562 memcpy(filename + namelen, ".pfb", 5);
563 data = fontfilecache_LoadFile(filename, false, &datasize);
567 memcpy(filename + namelen, ".afm", 5);
568 afm.data = fontfilecache_LoadFile(filename, false, &afm.size);
571 Font_Attach(font, &afm);
576 // FS_LoadFile being not-quiet should print an error :)
579 Con_DPrintf("Loading font %s face %i...\n", filename, _face);
581 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
582 if (status && _face != 0)
584 Con_Printf("Failed to load face %i of %s. Falling back to face 0\n", _face, name);
586 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, 0, (FT_Face*)&font->face);
591 Con_Printf("ERROR: can't create face for %s\n"
592 "Error %i\n", // TODO: error strings
594 Font_UnloadFont(font);
598 // add the attachments
599 for (i = 0; i < font->attachmentcount; ++i)
602 memset(&args, 0, sizeof(args));
603 args.flags = FT_OPEN_MEMORY;
604 args.memory_base = (const FT_Byte*)font->attachments[i].data;
605 args.memory_size = font->attachments[i].size;
606 if (qFT_Attach_Stream((FT_Face)font->face, &args))
607 Con_Printf("Failed to add attachment %u to %s\n", (unsigned)i, font->name);
610 memcpy(font->name, name, namelen+1);
611 font->image_font = false;
612 font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
616 static void Font_Postprocess_Update(ft2_font_t *fnt, int bpp, int w, int h)
619 float gausstable[2*POSTPROCESS_MAXRADIUS+1];
620 qboolean need_gauss = (!pp.buf || pp.blur != fnt->settings->blur || pp.shadowz != fnt->settings->shadowz);
621 qboolean need_circle = (!pp.buf || pp.outline != fnt->settings->outline || pp.shadowx != fnt->settings->shadowx || pp.shadowy != fnt->settings->shadowy);
622 pp.blur = fnt->settings->blur;
623 pp.outline = fnt->settings->outline;
624 pp.shadowx = fnt->settings->shadowx;
625 pp.shadowy = fnt->settings->shadowy;
626 pp.shadowz = fnt->settings->shadowz;
627 pp.outlinepadding_l = bound(0, ceil(pp.outline - pp.shadowx), POSTPROCESS_MAXRADIUS);
628 pp.outlinepadding_r = bound(0, ceil(pp.outline + pp.shadowx), POSTPROCESS_MAXRADIUS);
629 pp.outlinepadding_t = bound(0, ceil(pp.outline - pp.shadowy), POSTPROCESS_MAXRADIUS);
630 pp.outlinepadding_b = bound(0, ceil(pp.outline + pp.shadowy), POSTPROCESS_MAXRADIUS);
631 pp.blurpadding_lt = bound(0, ceil(pp.blur - pp.shadowz), POSTPROCESS_MAXRADIUS);
632 pp.blurpadding_rb = bound(0, ceil(pp.blur + pp.shadowz), POSTPROCESS_MAXRADIUS);
633 pp.padding_l = pp.blurpadding_lt + pp.outlinepadding_l;
634 pp.padding_r = pp.blurpadding_rb + pp.outlinepadding_r;
635 pp.padding_t = pp.blurpadding_lt + pp.outlinepadding_t;
636 pp.padding_b = pp.blurpadding_rb + pp.outlinepadding_b;
640 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
641 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));
642 for(x = -pp.blurpadding_rb; x <= pp.blurpadding_lt; ++x)
643 sum += gausstable[POSTPROCESS_MAXRADIUS+x];
644 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
645 pp.gausstable[POSTPROCESS_MAXRADIUS+x] = floor(gausstable[POSTPROCESS_MAXRADIUS+x] / sum * 255 + 0.5);
649 for(y = -POSTPROCESS_MAXRADIUS; y <= POSTPROCESS_MAXRADIUS; ++y)
650 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
652 float d = pp.outline + 1 - sqrt(pow(x + pp.shadowx, 2) + pow(y + pp.shadowy, 2));
653 pp.circlematrix[POSTPROCESS_MAXRADIUS+y][POSTPROCESS_MAXRADIUS+x] = (d >= 1) ? 255 : (d <= 0) ? 0 : floor(d * 255 + 0.5);
656 pp.bufwidth = w + pp.padding_l + pp.padding_r;
657 pp.bufheight = h + pp.padding_t + pp.padding_b;
658 pp.bufpitch = pp.bufwidth;
659 needed = pp.bufwidth * pp.bufheight;
660 if(!pp.buf || pp.bufsize < needed * 2)
664 pp.bufsize = needed * 4;
665 pp.buf = (unsigned char *)Mem_Alloc(font_mempool, pp.bufsize);
666 pp.buf2 = pp.buf + needed;
670 static 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)
674 // calculate gauss table
675 Font_Postprocess_Update(fnt, bpp, w, h);
680 // perform operation, not exceeding the passed padding values,
681 // but possibly reducing them
682 *pad_l = min(*pad_l, pp.padding_l);
683 *pad_r = min(*pad_r, pp.padding_r);
684 *pad_t = min(*pad_t, pp.padding_t);
685 *pad_b = min(*pad_b, pp.padding_b);
687 // outline the font (RGBA only)
688 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
690 // this is like mplayer subtitle rendering
691 // bbuffer, bitmap buffer: this is our font
692 // abuffer, alpha buffer: this is pp.buf
693 // tmp: this is pp.buf2
695 // create outline buffer
696 memset(pp.buf, 0, pp.bufwidth * pp.bufheight);
697 for(y = -*pad_t; y < h + *pad_b; ++y)
698 for(x = -*pad_l; x < w + *pad_r; ++x)
700 int x1 = max(-x, -pp.outlinepadding_r);
701 int y1 = max(-y, -pp.outlinepadding_b);
702 int x2 = min(pp.outlinepadding_l, w-1-x);
703 int y2 = min(pp.outlinepadding_t, h-1-y);
707 for(my = y1; my <= y2; ++my)
708 for(mx = x1; mx <= x2; ++mx)
710 cur = pp.circlematrix[POSTPROCESS_MAXRADIUS+my][POSTPROCESS_MAXRADIUS+mx] * (int)imagedata[(x+mx) * bpp + pitch * (y+my) + (bpp - 1)];
714 pp.buf[((x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t))] = (highest + 128) / 255;
717 // blur the outline buffer
718 if(pp.blur > 0 || pp.shadowz != 0)
721 for(y = 0; y < pp.bufheight; ++y)
722 for(x = 0; x < pp.bufwidth; ++x)
724 int x1 = max(-x, -pp.blurpadding_rb);
725 int x2 = min(pp.blurpadding_lt, pp.bufwidth-1-x);
728 for(mx = x1; mx <= x2; ++mx)
729 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+mx] * (int)pp.buf[(x+mx) + pp.bufpitch * y];
730 pp.buf2[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
734 for(y = 0; y < pp.bufheight; ++y)
735 for(x = 0; x < pp.bufwidth; ++x)
737 int y1 = max(-y, -pp.blurpadding_rb);
738 int y2 = min(pp.blurpadding_lt, pp.bufheight-1-y);
741 for(my = y1; my <= y2; ++my)
742 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+my] * (int)pp.buf2[x + pp.bufpitch * (y+my)];
743 pp.buf[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
747 // paste the outline below the font
748 for(y = -*pad_t; y < h + *pad_b; ++y)
749 for(x = -*pad_l; x < w + *pad_r; ++x)
751 unsigned char outlinealpha = pp.buf[(x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t)];
754 unsigned char oldalpha = imagedata[x * bpp + pitch * y + (bpp - 1)];
755 // a' = 1 - (1 - a1) (1 - a2)
756 unsigned char newalpha = 255 - ((255 - (int)outlinealpha) * (255 - (int)oldalpha)) / 255; // this is >= oldalpha
757 // c' = (a2 c2 - a1 a2 c1 + a1 c1) / a' = (a2 c2 + a1 (1 - a2) c1) / a'
758 unsigned char oldfactor = (255 * (int)oldalpha) / newalpha;
759 //unsigned char outlinefactor = ((255 - oldalpha) * (int)outlinealpha) / newalpha;
761 for(i = 0; i < bpp-1; ++i)
763 unsigned char c = imagedata[x * bpp + pitch * y + i];
764 c = (c * (int)oldfactor) / 255 /* + outlinecolor[i] * (int)outlinefactor */;
765 imagedata[x * bpp + pitch * y + i] = c;
767 imagedata[x * bpp + pitch * y + (bpp - 1)] = newalpha;
769 //imagedata[x * bpp + pitch * y + (bpp - 1)] |= 0x80;
775 // perform operation, not exceeding the passed padding values,
776 // but possibly reducing them
777 *pad_l = min(*pad_l, pp.padding_l);
778 *pad_r = min(*pad_r, pp.padding_r);
779 *pad_t = min(*pad_t, pp.padding_t);
780 *pad_b = min(*pad_b, pp.padding_b);
784 // just calculate parameters
785 *pad_l = pp.padding_l;
786 *pad_r = pp.padding_r;
787 *pad_t = pp.padding_t;
788 *pad_b = pp.padding_b;
792 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size);
793 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
794 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only)
797 ft2_font_map_t *fmap, temp;
798 int gpad_l, gpad_r, gpad_t, gpad_b;
800 if (!(size > 0.001f && size < 1000.0f))
805 if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
808 for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
810 if (!font->font_maps[map_index])
812 // if a similar size has already been loaded, ignore this one
813 //abs(font->font_maps[map_index]->size - size) < 4
814 if (font->font_maps[map_index]->size == size)
818 if (map_index >= MAX_FONT_SIZES)
823 if (font->image_font)
824 fontface = (FT_Face)font->next->face;
826 fontface = (FT_Face)font->face;
827 return (Font_SearchSize(font, fontface, size) > 0);
830 Font_Postprocess(font, NULL, 0, 4, size*2, size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
832 memset(&temp, 0, sizeof(temp));
834 temp.glyphSize = size*2 + max(gpad_l + gpad_r, gpad_t + gpad_b);
835 if (!(r_font_nonpoweroftwo.integer && vid.support.arb_texture_non_power_of_two))
836 temp.glyphSize = CeilPowerOf2(temp.glyphSize);
837 temp.sfx = (1.0/64.0)/(double)size;
838 temp.sfy = (1.0/64.0)/(double)size;
839 temp.intSize = -1; // negative value: LoadMap must search now :)
840 if (!Font_LoadMap(font, &temp, 0, &fmap))
842 Con_Printf("ERROR: can't load the first character map for %s\n"
845 Font_UnloadFont(font);
848 font->font_maps[map_index] = temp.next;
850 fmap->sfx = temp.sfx;
851 fmap->sfy = temp.sfy;
853 // load the default kerning vector:
854 if (font->has_kerning)
858 for (l = 0; l < 256; ++l)
860 for (r = 0; r < 256; ++r)
863 ul = qFT_Get_Char_Index((FT_Face)font->face, l);
864 ur = qFT_Get_Char_Index((FT_Face)font->face, r);
865 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
867 fmap->kerning.kerning[l][r][0] = 0;
868 fmap->kerning.kerning[l][r][1] = 0;
872 fmap->kerning.kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
873 fmap->kerning.kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
881 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
884 float value = 1000000;
886 int matchsize = -10000;
888 float fsize_x, fsize_y;
889 ft2_font_map_t **maps = font->font_maps;
891 fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value;
893 fsize_x = *outw * vid.width / vid_conwidth.value;
895 fsize_y = *outh * vid.height / vid_conheight.value;
900 fsize_x = fsize_y = 16;
910 for (m = 0; m < MAX_FONT_SIZES; ++m)
914 // "round up" to the bigger size if two equally-valued matches exist
915 nval = 0.5 * (fabs(maps[m]->size - fsize_x) + fabs(maps[m]->size - fsize_y));
916 if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
920 matchsize = maps[m]->size;
921 if (value == 0) // there is no better match
925 if (value <= r_font_size_snapping.value)
927 // do NOT keep the aspect for perfect rendering
928 if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
929 if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width;
934 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
936 if (index < 0 || index >= MAX_FONT_SIZES)
938 return font->font_maps[index];
941 static qboolean Font_SetSize(ft2_font_t *font, float w, float h)
943 if (font->currenth == h &&
944 ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
945 font->currentw == w)) // same size has been requested
949 // sorry, but freetype doesn't seem to care about other sizes
952 if (font->image_font)
954 if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
959 if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
967 qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
969 ft2_font_map_t *fmap;
970 if (!font->has_kerning || !r_font_kerning.integer)
972 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
974 fmap = font->font_maps[map_index];
977 if (left < 256 && right < 256)
979 //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
980 // quick-kerning, be aware of the size: scale it
981 if (outx) *outx = fmap->kerning.kerning[left][right][0];// * (w / (float)fmap->size);
982 if (outy) *outy = fmap->kerning.kerning[left][right][1];// * (h / (float)fmap->size);
990 //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
992 if (!Font_SetSize(font, w, h))
994 // this deserves an error message
995 Con_Printf("Failed to get kerning for %s\n", font->name);
998 ul = qFT_Get_Char_Index(font->face, left);
999 ur = qFT_Get_Char_Index(font->face, right);
1000 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
1002 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);
1003 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);
1007 if (!Font_SetSize(font, fmap->intSize, fmap->intSize))
1009 // this deserves an error message
1010 Con_Printf("Failed to get kerning for %s\n", font->name);
1013 ul = qFT_Get_Char_Index((FT_Face)font->face, left);
1014 ur = qFT_Get_Char_Index((FT_Face)font->face, right);
1015 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
1017 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);// * (w / (float)fmap->size);
1018 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);// * (h / (float)fmap->size);
1025 qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
1027 return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
1030 static void UnloadMapRec(ft2_font_map_t *map)
1034 //Draw_FreePic(map->pic); // FIXME: refcounting needed...
1038 UnloadMapRec(map->next);
1042 void Font_UnloadFont(ft2_font_t *font)
1048 Font_UnloadFont(font->next);
1050 if (font->attachments && font->attachmentcount)
1052 for (i = 0; i < (int)font->attachmentcount; ++i) {
1053 if (font->attachments[i].data)
1054 fontfilecache_Free(font->attachments[i].data);
1056 Mem_Free(font->attachments);
1057 font->attachmentcount = 0;
1058 font->attachments = NULL;
1060 for (i = 0; i < MAX_FONT_SIZES; ++i)
1062 if (font->font_maps[i])
1064 UnloadMapRec(font->font_maps[i]);
1065 font->font_maps[i] = NULL;
1072 qFT_Done_Face((FT_Face)font->face);
1077 fontfilecache_Free(font->data);
1082 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size)
1084 float intSize = size;
1087 if (!Font_SetSize(font, intSize, intSize))
1089 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, size, intSize);
1092 if ((fontface->size->metrics.height>>6) <= size)
1096 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, size);
1103 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
1105 char map_identifier[MAX_QPATH];
1106 unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
1107 unsigned char *data = NULL;
1111 FT_Int32 load_flags;
1112 int gpad_l, gpad_r, gpad_t, gpad_b;
1116 int gR, gC; // glyph position: row and column
1118 ft2_font_map_t *map, *next;
1119 ft2_font_t *usefont;
1123 int bytesPerPixel = 4; // change the conversion loop too if you change this!
1128 if (r_font_use_alpha_textures.integer)
1131 if (font->image_font)
1132 fontface = (FT_Face)font->next->face;
1134 fontface = (FT_Face)font->face;
1136 switch(font->settings->antialias)
1139 switch(font->settings->hinting)
1142 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1146 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1150 load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1156 switch(font->settings->hinting)
1159 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1162 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
1165 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1169 load_flags = FT_LOAD_TARGET_NORMAL;
1175 //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
1177 if (font->image_font && mapstart->intSize < 0)
1178 mapstart->intSize = mapstart->size;
1179 if (mapstart->intSize < 0)
1182 mapstart->intSize = mapstart->size;
1185 if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1187 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
1190 if ((fontface->size->metrics.height>>6) <= mapstart->size)
1192 if (mapstart->intSize < 2)
1194 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
1197 --mapstart->intSize;
1200 if ((mapstart->intSize = Font_SearchSize(font, fontface, mapstart->size)) <= 0)
1202 Con_DPrintf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
1205 if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1207 Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
1211 map = (ft2_font_map_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
1214 Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
1218 // create a totally unique name for this map, then we will use it to make a unique cachepic_t to avoid redundant textures
1219 dpsnprintf(map_identifier, sizeof(map_identifier),
1220 "%s_cache_%g_%d_%g_%g_%g_%g_%g_%u",
1222 (double) mapstart->intSize,
1224 (double) font->settings->blur,
1225 (double) font->settings->outline,
1226 (double) font->settings->shadowx,
1227 (double) font->settings->shadowy,
1228 (double) font->settings->shadowz,
1231 // create a cachepic_t from the data now, or reuse an existing one
1232 map->pic = Draw_CachePic_Flags(map_identifier, CACHEPICFLAG_QUIET);
1233 if (developer_font.integer)
1235 if (map->pic->tex == r_texture_notexture)
1236 Con_Printf("Generating font map %s (size: %.1f MB)\n", map_identifier, mapstart->glyphSize * (256 * 4 / 1048576.0) * mapstart->glyphSize);
1238 Con_Printf("Using cached font map %s (size: %.1f MB)\n", map_identifier, mapstart->glyphSize * (256 * 4 / 1048576.0) * mapstart->glyphSize);
1241 Font_Postprocess(font, NULL, 0, bytesPerPixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
1243 // copy over the information
1244 map->size = mapstart->size;
1245 map->intSize = mapstart->intSize;
1246 map->glyphSize = mapstart->glyphSize;
1247 map->sfx = mapstart->sfx;
1248 map->sfy = mapstart->sfy;
1250 pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
1251 if (map->pic->tex == r_texture_notexture)
1253 data = (unsigned char *)Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
1256 Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
1260 // initialize as white texture with zero alpha
1262 while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
1264 if (bytesPerPixel == 4)
1274 memset(map->width_of, 0, sizeof(map->width_of));
1277 map->start = mapidx * FONT_CHARS_PER_MAP;
1279 while(next->next && next->next->start < map->start)
1281 map->next = next->next;
1286 for (ch = map->start;
1287 ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
1290 FT_ULong glyphIndex;
1294 unsigned char *imagedata = NULL, *dst, *src;
1295 glyph_slot_t *mapglyph;
1297 int pad_l, pad_r, pad_t, pad_b;
1299 mapch = ch - map->start;
1301 if (developer_font.integer)
1302 Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
1305 if (gC >= FONT_CHARS_PER_LINE)
1307 gC -= FONT_CHARS_PER_LINE;
1313 imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
1314 imagedata += gpad_t * pitch + gpad_l * bytesPerPixel;
1316 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
1317 // we need the glyphIndex
1318 face = (FT_Face)font->face;
1320 if (font->image_font && mapch == ch && img_fontmap[mapch])
1322 map->glyphs[mapch].image = true;
1325 glyphIndex = qFT_Get_Char_Index(face, ch);
1326 if (glyphIndex == 0)
1328 // by convention, 0 is the "missing-glyph"-glyph
1329 // try to load from a fallback font
1330 for(usefont = font->next; usefont != NULL; usefont = usefont->next)
1332 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
1335 face = (FT_Face)usefont->face;
1336 glyphIndex = qFT_Get_Char_Index(face, ch);
1337 if (glyphIndex == 0)
1339 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1346 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1347 // now we let it use the "missing-glyph"-glyph
1348 face = (FT_Face)font->face;
1356 face = (FT_Face)font->face;
1357 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1360 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1361 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1366 glyph = face->glyph;
1367 bmp = &glyph->bitmap;
1372 if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
1373 Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
1374 if (w > map->glyphSize)
1375 w = map->glyphSize - gpad_l - gpad_r;
1376 if (h > map->glyphSize)
1382 switch (bmp->pixel_mode)
1384 case FT_PIXEL_MODE_MONO:
1385 if (developer_font.integer)
1386 Con_DPrint("glyphinfo: Pixel Mode: MONO\n");
1388 case FT_PIXEL_MODE_GRAY2:
1389 if (developer_font.integer)
1390 Con_DPrint("glyphinfo: Pixel Mode: GRAY2\n");
1392 case FT_PIXEL_MODE_GRAY4:
1393 if (developer_font.integer)
1394 Con_DPrint("glyphinfo: Pixel Mode: GRAY4\n");
1396 case FT_PIXEL_MODE_GRAY:
1397 if (developer_font.integer)
1398 Con_DPrint("glyphinfo: Pixel Mode: GRAY\n");
1401 if (developer_font.integer)
1402 Con_DPrintf("glyphinfo: Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1404 Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1407 for (y = 0; y < h; ++y)
1409 dst = imagedata + y * pitch;
1410 src = bmp->buffer + y * bmp->pitch;
1412 switch (bmp->pixel_mode)
1414 case FT_PIXEL_MODE_MONO:
1415 dst += bytesPerPixel - 1; // shift to alpha byte
1416 for (x = 0; x < bmp->width; x += 8)
1418 unsigned char ch = *src++;
1419 *dst = 255 * !!((ch & 0x80) >> 7); dst += bytesPerPixel;
1420 *dst = 255 * !!((ch & 0x40) >> 6); dst += bytesPerPixel;
1421 *dst = 255 * !!((ch & 0x20) >> 5); dst += bytesPerPixel;
1422 *dst = 255 * !!((ch & 0x10) >> 4); dst += bytesPerPixel;
1423 *dst = 255 * !!((ch & 0x08) >> 3); dst += bytesPerPixel;
1424 *dst = 255 * !!((ch & 0x04) >> 2); dst += bytesPerPixel;
1425 *dst = 255 * !!((ch & 0x02) >> 1); dst += bytesPerPixel;
1426 *dst = 255 * !!((ch & 0x01) >> 0); dst += bytesPerPixel;
1429 case FT_PIXEL_MODE_GRAY2:
1430 dst += bytesPerPixel - 1; // shift to alpha byte
1431 for (x = 0; x < bmp->width; x += 4)
1433 unsigned char ch = *src++;
1434 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1435 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1436 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1437 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1440 case FT_PIXEL_MODE_GRAY4:
1441 dst += bytesPerPixel - 1; // shift to alpha byte
1442 for (x = 0; x < bmp->width; x += 2)
1444 unsigned char ch = *src++;
1445 *dst = ( ((ch & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
1446 *dst = ( ((ch & 0x0F) ) * 0x11); dst += bytesPerPixel;
1449 case FT_PIXEL_MODE_GRAY:
1450 // in this case pitch should equal width
1451 for (tp = 0; tp < bmp->pitch; ++tp)
1452 dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
1454 //memcpy((void*)dst, (void*)src, bmp->pitch);
1455 //dst += bmp->pitch;
1466 Font_Postprocess(font, imagedata, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1474 Font_Postprocess(font, NULL, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1478 // now fill map->glyphs[ch - map->start]
1479 mapglyph = &map->glyphs[mapch];
1483 // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1485 double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1486 //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1487 double advance = (glyph->advance.x / 64.0) / map->size;
1488 //double mWidth = (glyph->metrics.width >> 6) / map->size;
1489 //double mHeight = (glyph->metrics.height >> 6) / map->size;
1491 mapglyph->txmin = ( (double)(gC * map->glyphSize) + (double)(gpad_l - pad_l) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1492 mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1493 mapglyph->tymin = ( (double)(gR * map->glyphSize) + (double)(gpad_r - pad_r) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1494 mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1495 //mapglyph->vxmin = bearingX;
1496 //mapglyph->vxmax = bearingX + mWidth;
1497 mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
1498 mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
1499 //mapglyph->vymin = -bearingY;
1500 //mapglyph->vymax = mHeight - bearingY;
1501 mapglyph->vymin = (-glyph->bitmap_top - pad_t) / map->size;
1502 mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
1503 //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);
1504 //mapglyph->advance_x = advance * usefont->size;
1505 //mapglyph->advance_x = advance;
1506 mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1507 mapglyph->advance_y = 0;
1509 if (developer_font.integer)
1511 Con_DPrintf("glyphinfo: Glyph: %lu at (%i, %i)\n", (unsigned long)ch, gC, gR);
1512 Con_DPrintf("glyphinfo: %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1513 if (ch >= 32 && ch <= 128)
1514 Con_DPrintf("glyphinfo: Character: %c\n", (int)ch);
1515 Con_DPrintf("glyphinfo: Vertex info:\n");
1516 Con_DPrintf("glyphinfo: X: ( %f -- %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1517 Con_DPrintf("glyphinfo: Y: ( %f -- %f )\n", mapglyph->vymin, mapglyph->vymax);
1518 Con_DPrintf("glyphinfo: Texture info:\n");
1519 Con_DPrintf("glyphinfo: S: ( %f -- %f )\n", mapglyph->txmin, mapglyph->txmax);
1520 Con_DPrintf("glyphinfo: T: ( %f -- %f )\n", mapglyph->tymin, mapglyph->tymax);
1521 Con_DPrintf("glyphinfo: Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1524 map->glyphs[mapch].image = false;
1527 if (map->pic->tex == r_texture_notexture)
1529 int w = map->glyphSize * FONT_CHARS_PER_LINE;
1530 int h = map->glyphSize * FONT_CHAR_LINES;
1532 // abuse the Draw_CachePic system to keep track of this texture
1533 tex = R_LoadTexture2D(drawtexturepool, map_identifier, w, h, data, r_font_use_alpha_textures.integer ? TEXTYPE_ALPHA : TEXTYPE_RGBA, TEXF_ALPHA | (r_font_compress.integer > 0 ? TEXF_COMPRESS : 0), -1, NULL);
1534 // if tex is NULL for any reason, the pic->tex will remain set to r_texture_notexture
1536 map->pic->tex = tex;
1538 if (r_font_diskcache.integer >= 1)
1540 // swap to BGRA for tga writing...
1544 for (x = 0;x < s;x++)
1547 data[x*4+0] = data[x*4+2];
1550 Image_WriteTGABGRA(va(vabuf, sizeof(vabuf), "%s.tga", map_identifier), w, h, data);
1552 if (r_font_compress.integer && qglGetCompressedTexImageARB && tex)
1553 R_SaveTextureDDSFile(tex, va(vabuf, sizeof(vabuf), "dds/%s.dds", map_identifier), r_texture_dds_save.integer < 2, true);
1561 if (map->pic->tex == r_texture_notexture)
1563 // if the first try isn't successful, keep it with a broken texture
1564 // otherwise we retry to load it every single frame where ft2 rendering is used
1565 // this would be bad...
1566 // only `data' must be freed
1567 Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
1568 font->name, mapstart->size, mapidx);
1576 qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
1578 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1580 // the first map must have been loaded already
1581 if (!font->font_maps[map_index])
1583 return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
1586 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1588 while (start && start->start + FONT_CHARS_PER_MAP <= ch)
1589 start = start->next;
1590 if (start && start->start > ch)