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 developer_font = {CVAR_SAVE, "developer_font", "0", "prints debug messages about fonts"};
45 ================================================================================
46 Function definitions. Taken from the freetype2 headers.
47 ================================================================================
52 (*qFT_Init_FreeType)( FT_Library *alibrary );
54 (*qFT_Done_FreeType)( FT_Library library );
57 (*qFT_New_Face)( FT_Library library,
58 const char* filepathname,
63 (*qFT_New_Memory_Face)( FT_Library library,
64 const FT_Byte* file_base,
69 (*qFT_Done_Face)( FT_Face face );
71 (*qFT_Select_Size)( FT_Face face,
72 FT_Int strike_index );
74 (*qFT_Request_Size)( FT_Face face,
75 FT_Size_Request req );
77 (*qFT_Set_Char_Size)( FT_Face face,
78 FT_F26Dot6 char_width,
79 FT_F26Dot6 char_height,
80 FT_UInt horz_resolution,
81 FT_UInt vert_resolution );
83 (*qFT_Set_Pixel_Sizes)( FT_Face face,
85 FT_UInt pixel_height );
87 (*qFT_Load_Glyph)( FT_Face face,
89 FT_Int32 load_flags );
91 (*qFT_Load_Char)( FT_Face face,
93 FT_Int32 load_flags );
95 (*qFT_Get_Char_Index)( FT_Face face,
98 (*qFT_Render_Glyph)( FT_GlyphSlot slot,
99 FT_Render_Mode render_mode );
100 FT_EXPORT( FT_Error )
101 (*qFT_Get_Kerning)( FT_Face face,
105 FT_Vector *akerning );
106 FT_EXPORT( FT_Error )
107 (*qFT_Attach_Stream)( FT_Face face,
108 FT_Open_Args* parameters );
110 ================================================================================
111 Support for dynamically loading the FreeType2 library
112 ================================================================================
115 static dllfunction_t ft2funcs[] =
117 {"FT_Init_FreeType", (void **) &qFT_Init_FreeType},
118 {"FT_Done_FreeType", (void **) &qFT_Done_FreeType},
119 //{"FT_New_Face", (void **) &qFT_New_Face},
120 {"FT_New_Memory_Face", (void **) &qFT_New_Memory_Face},
121 {"FT_Done_Face", (void **) &qFT_Done_Face},
122 {"FT_Select_Size", (void **) &qFT_Select_Size},
123 {"FT_Request_Size", (void **) &qFT_Request_Size},
124 {"FT_Set_Char_Size", (void **) &qFT_Set_Char_Size},
125 {"FT_Set_Pixel_Sizes", (void **) &qFT_Set_Pixel_Sizes},
126 {"FT_Load_Glyph", (void **) &qFT_Load_Glyph},
127 {"FT_Load_Char", (void **) &qFT_Load_Char},
128 {"FT_Get_Char_Index", (void **) &qFT_Get_Char_Index},
129 {"FT_Render_Glyph", (void **) &qFT_Render_Glyph},
130 {"FT_Get_Kerning", (void **) &qFT_Get_Kerning},
131 {"FT_Attach_Stream", (void **) &qFT_Attach_Stream},
135 /// Handle for FreeType2 DLL
136 static dllhandle_t ft2_dll = NULL;
138 /// Memory pool for fonts
139 static mempool_t *font_mempool= NULL;
141 /// FreeType library handle
142 static FT_Library font_ft2lib = NULL;
144 #define POSTPROCESS_MAXRADIUS 8
147 unsigned char *buf, *buf2;
148 int bufsize, bufwidth, bufheight, bufpitch;
149 float blur, outline, shadowx, shadowy, shadowz;
150 int padding_t, padding_b, padding_l, padding_r, blurpadding_lt, blurpadding_rb, outlinepadding_t, outlinepadding_b, outlinepadding_l, outlinepadding_r;
151 unsigned char circlematrix[2*POSTPROCESS_MAXRADIUS+1][2*POSTPROCESS_MAXRADIUS+1];
152 unsigned char gausstable[2*POSTPROCESS_MAXRADIUS+1];
155 static font_postprocess_t pp;
157 typedef struct fontfilecache_s
162 char path[MAX_QPATH];
165 #define MAX_FONTFILES 8
166 static fontfilecache_t fontfiles[MAX_FONTFILES];
167 static const unsigned char *fontfilecache_LoadFile(const char *path, qboolean quiet, fs_offset_t *filesizepointer)
172 for(i = 0; i < MAX_FONTFILES; ++i)
174 if(fontfiles[i].refcount > 0)
175 if(!strcmp(path, fontfiles[i].path))
177 *filesizepointer = fontfiles[i].len;
178 ++fontfiles[i].refcount;
179 return fontfiles[i].buf;
183 buf = FS_LoadFile(path, font_mempool, quiet, filesizepointer);
186 for(i = 0; i < MAX_FONTFILES; ++i)
187 if(fontfiles[i].refcount <= 0)
189 strlcpy(fontfiles[i].path, path, sizeof(fontfiles[i].path));
190 fontfiles[i].len = *filesizepointer;
191 fontfiles[i].buf = buf;
192 fontfiles[i].refcount = 1;
199 static void fontfilecache_Free(const unsigned char *buf)
202 for(i = 0; i < MAX_FONTFILES; ++i)
204 if(fontfiles[i].refcount > 0)
205 if(fontfiles[i].buf == buf)
207 if(--fontfiles[i].refcount <= 0)
209 Mem_Free(fontfiles[i].buf);
210 fontfiles[i].buf = NULL;
215 // if we get here, it used regular allocation
216 Mem_Free((void *) buf);
218 static void fontfilecache_FreeAll(void)
221 for(i = 0; i < MAX_FONTFILES; ++i)
223 if(fontfiles[i].refcount > 0)
224 Mem_Free(fontfiles[i].buf);
225 fontfiles[i].buf = NULL;
226 fontfiles[i].refcount = 0;
234 Unload the FreeType2 DLL
237 void Font_CloseLibrary (void)
239 fontfilecache_FreeAll();
241 Mem_FreePool(&font_mempool);
242 if (font_ft2lib && qFT_Done_FreeType)
244 qFT_Done_FreeType(font_ft2lib);
247 Sys_UnloadLibrary (&ft2_dll);
255 Try to load the FreeType2 DLL
258 qboolean Font_OpenLibrary (void)
260 const char* dllnames [] =
265 #elif defined(MACOSX)
266 "libfreetype.6.dylib",
275 if (r_font_disable_freetype.integer)
283 if (!Sys_LoadLibrary (dllnames, &ft2_dll, ft2funcs))
292 Initialize the freetype2 font subsystem
296 void font_start(void)
298 if (!Font_OpenLibrary())
301 if (qFT_Init_FreeType(&font_ft2lib))
303 Con_Print("ERROR: Failed to initialize the FreeType2 library!\n");
308 font_mempool = Mem_AllocPool("FONT", 0, NULL);
311 Con_Print("ERROR: Failed to allocate FONT memory pool!\n");
317 void font_shutdown(void)
320 for (i = 0; i < dp_fonts.maxsize; ++i)
322 if (dp_fonts.f[i].ft2)
324 Font_UnloadFont(dp_fonts.f[i].ft2);
325 dp_fonts.f[i].ft2 = NULL;
331 void font_newmap(void)
337 Cvar_RegisterVariable(&r_font_disable_freetype);
338 Cvar_RegisterVariable(&r_font_use_alpha_textures);
339 Cvar_RegisterVariable(&r_font_size_snapping);
340 Cvar_RegisterVariable(&r_font_kerning);
341 Cvar_RegisterVariable(&r_font_diskcache);
342 Cvar_RegisterVariable(&r_font_compress);
343 Cvar_RegisterVariable(&developer_font);
345 // let's open it at startup already
350 ================================================================================
351 Implementation of a more or less lazy font loading and rendering code.
352 ================================================================================
355 #include "ft2_fontdefs.h"
357 ft2_font_t *Font_Alloc(void)
361 return (ft2_font_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_t));
364 qboolean Font_Attach(ft2_font_t *font, ft2_attachment_t *attachment)
366 ft2_attachment_t *na;
368 font->attachmentcount++;
369 na = (ft2_attachment_t*)Mem_Alloc(font_mempool, sizeof(font->attachments[0]) * font->attachmentcount);
372 if (font->attachments && font->attachmentcount > 1)
374 memcpy(na, font->attachments, sizeof(font->attachments[0]) * (font->attachmentcount - 1));
375 Mem_Free(font->attachments);
377 memcpy(na + sizeof(font->attachments[0]) * (font->attachmentcount - 1), attachment, sizeof(*attachment));
378 font->attachments = na;
382 float Font_VirtualToRealSize(float sz)
390 //vw = ((vid.width > 0) ? vid.width : vid_width.value);
391 vh = ((vid.height > 0) ? vid.height : vid_height.value);
392 // now try to scale to our actual size:
393 sn = sz * vh / vid_conheight.value;
395 if ( sn - (float)si >= 0.5 )
400 float Font_SnapTo(float val, float snapwidth)
402 return floor(val / snapwidth + 0.5f) * snapwidth;
405 static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font);
406 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only);
407 qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt)
410 ft2_font_t *ft2, *fbfont, *fb;
419 // check if a fallback font has been specified, if it has been, and the
420 // font fails to load, use the image font as main font
421 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
423 if (dpfnt->fallbacks[i][0])
427 if (!Font_LoadFile(name, dpfnt->req_face, &dpfnt->settings, ft2))
429 if (i >= MAX_FONT_FALLBACKS)
435 strlcpy(ft2->name, name, sizeof(ft2->name));
436 ft2->image_font = true;
437 ft2->has_kerning = false;
441 ft2->image_font = false;
444 // attempt to load fallback fonts:
446 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
448 if (!dpfnt->fallbacks[i][0])
450 if (! (fb = Font_Alloc()) )
452 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
456 if (!Font_LoadFile(dpfnt->fallbacks[i], dpfnt->fallback_faces[i], &dpfnt->settings, fb))
458 if(!FS_FileExists(va("%s.tga", dpfnt->fallbacks[i])))
459 if(!FS_FileExists(va("%s.png", dpfnt->fallbacks[i])))
460 if(!FS_FileExists(va("%s.jpg", dpfnt->fallbacks[i])))
461 if(!FS_FileExists(va("%s.pcx", dpfnt->fallbacks[i])))
462 Con_Printf("Failed to load font %s for fallback %i of font %s\n", dpfnt->fallbacks[i], i, name);
467 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
469 if (Font_LoadSize(fb, Font_VirtualToRealSize(dpfnt->req_sizes[s]), true))
474 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
479 // at least one size of the fallback font loaded successfully
485 if (fbfont == ft2 && ft2->image_font)
487 // no fallbacks were loaded successfully:
494 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
496 if (Font_LoadSize(ft2, Font_VirtualToRealSize(dpfnt->req_sizes[s]), false))
501 // loading failed for every requested size
502 Font_UnloadFont(ft2);
508 //Con_Printf("%i sizes loaded\n", count);
513 static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font)
516 char filename[MAX_QPATH];
519 const unsigned char *data;
520 fs_offset_t datasize;
522 memset(font, 0, sizeof(*font));
524 if (!Font_OpenLibrary())
526 if (!r_font_disable_freetype.integer)
528 Con_Printf("WARNING: can't open load font %s\n"
529 "You need the FreeType2 DLL to load font files\n",
535 font->settings = settings;
537 namelen = strlen(name);
539 // try load direct file
540 memcpy(filename, name, namelen+1);
541 data = fontfilecache_LoadFile(filename, false, &datasize);
545 memcpy(filename + namelen, ".ttf", 5);
546 data = fontfilecache_LoadFile(filename, false, &datasize);
551 memcpy(filename + namelen, ".otf", 5);
552 data = fontfilecache_LoadFile(filename, false, &datasize);
557 ft2_attachment_t afm;
559 memcpy(filename + namelen, ".pfb", 5);
560 data = fontfilecache_LoadFile(filename, false, &datasize);
564 memcpy(filename + namelen, ".afm", 5);
565 afm.data = fontfilecache_LoadFile(filename, false, &afm.size);
568 Font_Attach(font, &afm);
573 // FS_LoadFile being not-quiet should print an error :)
576 Con_DPrintf("Loading font %s face %i...\n", filename, _face);
578 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
579 if (status && _face != 0)
581 Con_Printf("Failed to load face %i of %s. Falling back to face 0\n", _face, name);
583 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, 0, (FT_Face*)&font->face);
588 Con_Printf("ERROR: can't create face for %s\n"
589 "Error %i\n", // TODO: error strings
591 Font_UnloadFont(font);
595 // add the attachments
596 for (i = 0; i < font->attachmentcount; ++i)
599 memset(&args, 0, sizeof(args));
600 args.flags = FT_OPEN_MEMORY;
601 args.memory_base = (const FT_Byte*)font->attachments[i].data;
602 args.memory_size = font->attachments[i].size;
603 if (qFT_Attach_Stream((FT_Face)font->face, &args))
604 Con_Printf("Failed to add attachment %u to %s\n", (unsigned)i, font->name);
607 memcpy(font->name, name, namelen+1);
608 font->image_font = false;
609 font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
613 void Font_Postprocess_Update(ft2_font_t *fnt, int bpp, int w, int h)
616 float gausstable[2*POSTPROCESS_MAXRADIUS+1];
617 qboolean need_gauss = (!pp.buf || pp.blur != fnt->settings->blur || pp.shadowz != fnt->settings->shadowz);
618 qboolean need_circle = (!pp.buf || pp.outline != fnt->settings->outline || pp.shadowx != fnt->settings->shadowx || pp.shadowy != fnt->settings->shadowy);
619 pp.blur = fnt->settings->blur;
620 pp.outline = fnt->settings->outline;
621 pp.shadowx = fnt->settings->shadowx;
622 pp.shadowy = fnt->settings->shadowy;
623 pp.shadowz = fnt->settings->shadowz;
624 pp.outlinepadding_l = bound(0, ceil(pp.outline - pp.shadowx), POSTPROCESS_MAXRADIUS);
625 pp.outlinepadding_r = bound(0, ceil(pp.outline + pp.shadowx), POSTPROCESS_MAXRADIUS);
626 pp.outlinepadding_t = bound(0, ceil(pp.outline - pp.shadowy), POSTPROCESS_MAXRADIUS);
627 pp.outlinepadding_b = bound(0, ceil(pp.outline + pp.shadowy), POSTPROCESS_MAXRADIUS);
628 pp.blurpadding_lt = bound(0, ceil(pp.blur - pp.shadowz), POSTPROCESS_MAXRADIUS);
629 pp.blurpadding_rb = bound(0, ceil(pp.blur + pp.shadowz), POSTPROCESS_MAXRADIUS);
630 pp.padding_l = pp.blurpadding_lt + pp.outlinepadding_l;
631 pp.padding_r = pp.blurpadding_rb + pp.outlinepadding_r;
632 pp.padding_t = pp.blurpadding_lt + pp.outlinepadding_t;
633 pp.padding_b = pp.blurpadding_rb + pp.outlinepadding_b;
637 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
638 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));
639 for(x = -pp.blurpadding_rb; x <= pp.blurpadding_lt; ++x)
640 sum += gausstable[POSTPROCESS_MAXRADIUS+x];
641 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
642 pp.gausstable[POSTPROCESS_MAXRADIUS+x] = floor(gausstable[POSTPROCESS_MAXRADIUS+x] / sum * 255 + 0.5);
646 for(y = -POSTPROCESS_MAXRADIUS; y <= POSTPROCESS_MAXRADIUS; ++y)
647 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
649 float d = pp.outline + 1 - sqrt(pow(x + pp.shadowx, 2) + pow(y + pp.shadowy, 2));
650 pp.circlematrix[POSTPROCESS_MAXRADIUS+y][POSTPROCESS_MAXRADIUS+x] = (d >= 1) ? 255 : (d <= 0) ? 0 : floor(d * 255 + 0.5);
653 pp.bufwidth = w + pp.padding_l + pp.padding_r;
654 pp.bufheight = h + pp.padding_t + pp.padding_b;
655 pp.bufpitch = pp.bufwidth;
656 needed = pp.bufwidth * pp.bufheight;
657 if(!pp.buf || pp.bufsize < needed * 2)
661 pp.bufsize = needed * 4;
662 pp.buf = (unsigned char *)Mem_Alloc(font_mempool, pp.bufsize);
663 pp.buf2 = pp.buf + needed;
667 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)
671 // calculate gauss table
672 Font_Postprocess_Update(fnt, bpp, w, h);
677 // perform operation, not exceeding the passed padding values,
678 // but possibly reducing them
679 *pad_l = min(*pad_l, pp.padding_l);
680 *pad_r = min(*pad_r, pp.padding_r);
681 *pad_t = min(*pad_t, pp.padding_t);
682 *pad_b = min(*pad_b, pp.padding_b);
684 // outline the font (RGBA only)
685 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
687 // this is like mplayer subtitle rendering
688 // bbuffer, bitmap buffer: this is our font
689 // abuffer, alpha buffer: this is pp.buf
690 // tmp: this is pp.buf2
692 // create outline buffer
693 memset(pp.buf, 0, pp.bufwidth * pp.bufheight);
694 for(y = -*pad_t; y < h + *pad_b; ++y)
695 for(x = -*pad_l; x < w + *pad_r; ++x)
697 int x1 = max(-x, -pp.outlinepadding_r);
698 int y1 = max(-y, -pp.outlinepadding_b);
699 int x2 = min(pp.outlinepadding_l, w-1-x);
700 int y2 = min(pp.outlinepadding_t, h-1-y);
704 for(my = y1; my <= y2; ++my)
705 for(mx = x1; mx <= x2; ++mx)
707 cur = pp.circlematrix[POSTPROCESS_MAXRADIUS+my][POSTPROCESS_MAXRADIUS+mx] * (int)imagedata[(x+mx) * bpp + pitch * (y+my) + (bpp - 1)];
711 pp.buf[((x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t))] = (highest + 128) / 255;
714 // blur the outline buffer
715 if(pp.blur > 0 || pp.shadowz != 0)
718 for(y = 0; y < pp.bufheight; ++y)
719 for(x = 0; x < pp.bufwidth; ++x)
721 int x1 = max(-x, -pp.blurpadding_rb);
722 int x2 = min(pp.blurpadding_lt, pp.bufwidth-1-x);
725 for(mx = x1; mx <= x2; ++mx)
726 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+mx] * (int)pp.buf[(x+mx) + pp.bufpitch * y];
727 pp.buf2[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
731 for(y = 0; y < pp.bufheight; ++y)
732 for(x = 0; x < pp.bufwidth; ++x)
734 int y1 = max(-y, -pp.blurpadding_rb);
735 int y2 = min(pp.blurpadding_lt, pp.bufheight-1-y);
738 for(my = y1; my <= y2; ++my)
739 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+my] * (int)pp.buf2[x + pp.bufpitch * (y+my)];
740 pp.buf[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
744 // paste the outline below the font
745 for(y = -*pad_t; y < h + *pad_b; ++y)
746 for(x = -*pad_l; x < w + *pad_r; ++x)
748 unsigned char outlinealpha = pp.buf[(x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t)];
751 unsigned char oldalpha = imagedata[x * bpp + pitch * y + (bpp - 1)];
752 // a' = 1 - (1 - a1) (1 - a2)
753 unsigned char newalpha = 255 - ((255 - (int)outlinealpha) * (255 - (int)oldalpha)) / 255; // this is >= oldalpha
754 // c' = (a2 c2 - a1 a2 c1 + a1 c1) / a' = (a2 c2 + a1 (1 - a2) c1) / a'
755 unsigned char oldfactor = (255 * (int)oldalpha) / newalpha;
756 //unsigned char outlinefactor = ((255 - oldalpha) * (int)outlinealpha) / newalpha;
758 for(i = 0; i < bpp-1; ++i)
760 unsigned char c = imagedata[x * bpp + pitch * y + i];
761 c = (c * (int)oldfactor) / 255 /* + outlinecolor[i] * (int)outlinefactor */;
762 imagedata[x * bpp + pitch * y + i] = c;
764 imagedata[x * bpp + pitch * y + (bpp - 1)] = newalpha;
766 //imagedata[x * bpp + pitch * y + (bpp - 1)] |= 0x80;
772 // perform operation, not exceeding the passed padding values,
773 // but possibly reducing them
774 *pad_l = min(*pad_l, pp.padding_l);
775 *pad_r = min(*pad_r, pp.padding_r);
776 *pad_t = min(*pad_t, pp.padding_t);
777 *pad_b = min(*pad_b, pp.padding_b);
781 // just calculate parameters
782 *pad_l = pp.padding_l;
783 *pad_r = pp.padding_r;
784 *pad_t = pp.padding_t;
785 *pad_b = pp.padding_b;
789 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size);
790 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
791 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only)
794 ft2_font_map_t *fmap, temp;
795 int gpad_l, gpad_r, gpad_t, gpad_b;
797 if (!(size > 0.001f && size < 1000.0f))
802 if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
805 for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
807 if (!font->font_maps[map_index])
809 // if a similar size has already been loaded, ignore this one
810 //abs(font->font_maps[map_index]->size - size) < 4
811 if (font->font_maps[map_index]->size == size)
815 if (map_index >= MAX_FONT_SIZES)
820 if (font->image_font)
821 fontface = (FT_Face)font->next->face;
823 fontface = (FT_Face)font->face;
824 return (Font_SearchSize(font, fontface, size) > 0);
827 Font_Postprocess(font, NULL, 0, 4, size*2, size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
829 memset(&temp, 0, sizeof(temp));
831 temp.glyphSize = CeilPowerOf2(size*2 + max(gpad_l + gpad_r, gpad_t + gpad_b));
832 temp.sfx = (1.0/64.0)/(double)size;
833 temp.sfy = (1.0/64.0)/(double)size;
834 temp.intSize = -1; // negative value: LoadMap must search now :)
835 if (!Font_LoadMap(font, &temp, 0, &fmap))
837 Con_Printf("ERROR: can't load the first character map for %s\n"
840 Font_UnloadFont(font);
843 font->font_maps[map_index] = temp.next;
845 fmap->sfx = temp.sfx;
846 fmap->sfy = temp.sfy;
848 // load the default kerning vector:
849 if (font->has_kerning)
853 for (l = 0; l < 256; ++l)
855 for (r = 0; r < 256; ++r)
858 ul = qFT_Get_Char_Index((FT_Face)font->face, l);
859 ur = qFT_Get_Char_Index((FT_Face)font->face, r);
860 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
862 fmap->kerning.kerning[l][r][0] = 0;
863 fmap->kerning.kerning[l][r][1] = 0;
867 fmap->kerning.kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
868 fmap->kerning.kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
876 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
879 float value = 1000000;
881 int matchsize = -10000;
883 float fsize_x, fsize_y;
884 ft2_font_map_t **maps = font->font_maps;
886 fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value;
888 fsize_x = *outw * vid.width / vid_conwidth.value;
890 fsize_y = *outh * vid.height / vid_conheight.value;
895 fsize_x = fsize_y = 16;
905 for (m = 0; m < MAX_FONT_SIZES; ++m)
909 // "round up" to the bigger size if two equally-valued matches exist
910 nval = 0.5 * (fabs(maps[m]->size - fsize_x) + fabs(maps[m]->size - fsize_y));
911 if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
915 matchsize = maps[m]->size;
916 if (value == 0) // there is no better match
920 if (value <= r_font_size_snapping.value)
922 // do NOT keep the aspect for perfect rendering
923 if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
924 if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width;
929 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
931 if (index < 0 || index >= MAX_FONT_SIZES)
933 return font->font_maps[index];
936 static qboolean Font_SetSize(ft2_font_t *font, float w, float h)
938 if (font->currenth == h &&
939 ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
940 font->currentw == w)) // same size has been requested
944 // sorry, but freetype doesn't seem to care about other sizes
947 if (font->image_font)
949 if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
954 if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
962 qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
964 ft2_font_map_t *fmap;
965 if (!font->has_kerning || !r_font_kerning.integer)
967 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
969 fmap = font->font_maps[map_index];
972 if (left < 256 && right < 256)
974 //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
975 // quick-kerning, be aware of the size: scale it
976 if (outx) *outx = fmap->kerning.kerning[left][right][0];// * (w / (float)fmap->size);
977 if (outy) *outy = fmap->kerning.kerning[left][right][1];// * (h / (float)fmap->size);
985 //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
987 if (!Font_SetSize(font, w, h))
989 // this deserves an error message
990 Con_Printf("Failed to get kerning for %s\n", font->name);
993 ul = qFT_Get_Char_Index(font->face, left);
994 ur = qFT_Get_Char_Index(font->face, right);
995 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
997 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);
998 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);
1002 if (!Font_SetSize(font, fmap->intSize, fmap->intSize))
1004 // this deserves an error message
1005 Con_Printf("Failed to get kerning for %s\n", font->name);
1008 ul = qFT_Get_Char_Index((FT_Face)font->face, left);
1009 ur = qFT_Get_Char_Index((FT_Face)font->face, right);
1010 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
1012 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);// * (w / (float)fmap->size);
1013 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);// * (h / (float)fmap->size);
1020 qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
1022 return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
1025 static void UnloadMapRec(ft2_font_map_t *map)
1029 //Draw_FreePic(map->pic); // FIXME: refcounting needed...
1033 UnloadMapRec(map->next);
1037 void Font_UnloadFont(ft2_font_t *font)
1043 Font_UnloadFont(font->next);
1045 if (font->attachments && font->attachmentcount)
1047 for (i = 0; i < (int)font->attachmentcount; ++i) {
1048 if (font->attachments[i].data)
1049 fontfilecache_Free(font->attachments[i].data);
1051 Mem_Free(font->attachments);
1052 font->attachmentcount = 0;
1053 font->attachments = NULL;
1055 for (i = 0; i < MAX_FONT_SIZES; ++i)
1057 if (font->font_maps[i])
1059 UnloadMapRec(font->font_maps[i]);
1060 font->font_maps[i] = NULL;
1067 qFT_Done_Face((FT_Face)font->face);
1072 fontfilecache_Free(font->data);
1077 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size)
1079 float intSize = size;
1082 if (!Font_SetSize(font, intSize, intSize))
1084 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, size, intSize);
1087 if ((fontface->size->metrics.height>>6) <= size)
1091 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, size);
1098 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
1100 char map_identifier[MAX_QPATH];
1101 unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
1102 unsigned char *data = NULL;
1106 FT_Int32 load_flags;
1107 int gpad_l, gpad_r, gpad_t, gpad_b;
1110 int gR, gC; // glyph position: row and column
1112 ft2_font_map_t *map, *next;
1113 ft2_font_t *usefont;
1117 int bytesPerPixel = 4; // change the conversion loop too if you change this!
1122 if (r_font_use_alpha_textures.integer)
1125 if (font->image_font)
1126 fontface = (FT_Face)font->next->face;
1128 fontface = (FT_Face)font->face;
1130 switch(font->settings->antialias)
1133 switch(font->settings->hinting)
1136 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1140 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1144 load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1150 switch(font->settings->hinting)
1153 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1156 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
1159 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1163 load_flags = FT_LOAD_TARGET_NORMAL;
1169 //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
1171 if (font->image_font && mapstart->intSize < 0)
1172 mapstart->intSize = mapstart->size;
1173 if (mapstart->intSize < 0)
1176 mapstart->intSize = mapstart->size;
1179 if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1181 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
1184 if ((fontface->size->metrics.height>>6) <= mapstart->size)
1186 if (mapstart->intSize < 2)
1188 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
1191 --mapstart->intSize;
1194 if ((mapstart->intSize = Font_SearchSize(font, fontface, mapstart->size)) <= 0)
1196 Con_DPrintf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
1199 if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1201 Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
1205 map = (ft2_font_map_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
1208 Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
1212 // create a totally unique name for this map, then we will use it to make a unique cachepic_t to avoid redundant textures
1213 dpsnprintf(map_identifier, sizeof(map_identifier),
1214 "%s_cache_%g_%d_%g_%g_%g_%g_%g_%u",
1218 font->settings->blur,
1219 font->settings->outline,
1220 font->settings->shadowx,
1221 font->settings->shadowy,
1222 font->settings->shadowz,
1223 (unsigned)map->start/FONT_CHARS_PER_MAP);
1225 // create a cachepic_t from the data now, or reuse an existing one
1226 map->pic = Draw_CachePic_Flags(map_identifier, CACHEPICFLAG_QUIET);
1228 Font_Postprocess(font, NULL, 0, bytesPerPixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
1230 // copy over the information
1231 map->size = mapstart->size;
1232 map->intSize = mapstart->intSize;
1233 map->glyphSize = mapstart->glyphSize;
1234 map->sfx = mapstart->sfx;
1235 map->sfy = mapstart->sfy;
1237 pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
1238 // if (map->pic->tex == r_texture_notexture)
1240 data = (unsigned char *)Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
1243 Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
1247 // initialize as white texture with zero alpha
1249 while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
1251 if (bytesPerPixel == 4)
1261 memset(map->width_of, 0, sizeof(map->width_of));
1264 map->start = mapidx * FONT_CHARS_PER_MAP;
1266 while(next->next && next->next->start < map->start)
1268 map->next = next->next;
1273 for (ch = map->start;
1274 ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
1277 FT_ULong glyphIndex;
1281 unsigned char *imagedata = NULL, *dst, *src;
1282 glyph_slot_t *mapglyph;
1284 int pad_l, pad_r, pad_t, pad_b;
1286 mapch = ch - map->start;
1288 if (developer_font.integer)
1289 Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
1292 if (gC >= FONT_CHARS_PER_LINE)
1294 gC -= FONT_CHARS_PER_LINE;
1300 imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
1301 imagedata += gpad_t * pitch + gpad_l * bytesPerPixel;
1303 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
1304 // we need the glyphIndex
1305 face = (FT_Face)font->face;
1307 if (font->image_font && mapch == ch && img_fontmap[mapch])
1309 map->glyphs[mapch].image = true;
1312 glyphIndex = qFT_Get_Char_Index(face, ch);
1313 if (glyphIndex == 0)
1315 // by convention, 0 is the "missing-glyph"-glyph
1316 // try to load from a fallback font
1317 for(usefont = font->next; usefont != NULL; usefont = usefont->next)
1319 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
1322 face = (FT_Face)usefont->face;
1323 glyphIndex = qFT_Get_Char_Index(face, ch);
1324 if (glyphIndex == 0)
1326 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1333 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1334 // now we let it use the "missing-glyph"-glyph
1335 face = (FT_Face)font->face;
1343 face = (FT_Face)font->face;
1344 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1347 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1348 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1353 glyph = face->glyph;
1354 bmp = &glyph->bitmap;
1359 if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
1360 Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
1361 if (w > map->glyphSize)
1362 w = map->glyphSize - gpad_l - gpad_r;
1363 if (h > map->glyphSize)
1369 switch (bmp->pixel_mode)
1371 case FT_PIXEL_MODE_MONO:
1372 if (developer_font.integer)
1373 Con_DPrint("glyphinfo: Pixel Mode: MONO\n");
1375 case FT_PIXEL_MODE_GRAY2:
1376 if (developer_font.integer)
1377 Con_DPrint("glyphinfo: Pixel Mode: GRAY2\n");
1379 case FT_PIXEL_MODE_GRAY4:
1380 if (developer_font.integer)
1381 Con_DPrint("glyphinfo: Pixel Mode: GRAY4\n");
1383 case FT_PIXEL_MODE_GRAY:
1384 if (developer_font.integer)
1385 Con_DPrint("glyphinfo: Pixel Mode: GRAY\n");
1388 if (developer_font.integer)
1389 Con_DPrintf("glyphinfo: Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1391 Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1394 for (y = 0; y < h; ++y)
1396 dst = imagedata + y * pitch;
1397 src = bmp->buffer + y * bmp->pitch;
1399 switch (bmp->pixel_mode)
1401 case FT_PIXEL_MODE_MONO:
1402 dst += bytesPerPixel - 1; // shift to alpha byte
1403 for (x = 0; x < bmp->width; x += 8)
1405 unsigned char ch = *src++;
1406 *dst = 255 * !!((ch & 0x80) >> 7); dst += bytesPerPixel;
1407 *dst = 255 * !!((ch & 0x40) >> 6); dst += bytesPerPixel;
1408 *dst = 255 * !!((ch & 0x20) >> 5); dst += bytesPerPixel;
1409 *dst = 255 * !!((ch & 0x10) >> 4); dst += bytesPerPixel;
1410 *dst = 255 * !!((ch & 0x08) >> 3); dst += bytesPerPixel;
1411 *dst = 255 * !!((ch & 0x04) >> 2); dst += bytesPerPixel;
1412 *dst = 255 * !!((ch & 0x02) >> 1); dst += bytesPerPixel;
1413 *dst = 255 * !!((ch & 0x01) >> 0); dst += bytesPerPixel;
1416 case FT_PIXEL_MODE_GRAY2:
1417 dst += bytesPerPixel - 1; // shift to alpha byte
1418 for (x = 0; x < bmp->width; x += 4)
1420 unsigned char ch = *src++;
1421 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1422 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1423 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1424 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1427 case FT_PIXEL_MODE_GRAY4:
1428 dst += bytesPerPixel - 1; // shift to alpha byte
1429 for (x = 0; x < bmp->width; x += 2)
1431 unsigned char ch = *src++;
1432 *dst = ( ((ch & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
1433 *dst = ( ((ch & 0x0F) ) * 0x11); dst += bytesPerPixel;
1436 case FT_PIXEL_MODE_GRAY:
1437 // in this case pitch should equal width
1438 for (tp = 0; tp < bmp->pitch; ++tp)
1439 dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
1441 //memcpy((void*)dst, (void*)src, bmp->pitch);
1442 //dst += bmp->pitch;
1453 Font_Postprocess(font, imagedata, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1461 Font_Postprocess(font, NULL, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1465 // now fill map->glyphs[ch - map->start]
1466 mapglyph = &map->glyphs[mapch];
1470 // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1472 double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1473 //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1474 double advance = (glyph->advance.x / 64.0) / map->size;
1475 //double mWidth = (glyph->metrics.width >> 6) / map->size;
1476 //double mHeight = (glyph->metrics.height >> 6) / map->size;
1478 mapglyph->txmin = ( (double)(gC * map->glyphSize) + (double)(gpad_l - pad_l) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1479 mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1480 mapglyph->tymin = ( (double)(gR * map->glyphSize) + (double)(gpad_r - pad_r) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1481 mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1482 //mapglyph->vxmin = bearingX;
1483 //mapglyph->vxmax = bearingX + mWidth;
1484 mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
1485 mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
1486 //mapglyph->vymin = -bearingY;
1487 //mapglyph->vymax = mHeight - bearingY;
1488 mapglyph->vymin = (-glyph->bitmap_top - pad_t) / map->size;
1489 mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
1490 //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);
1491 //mapglyph->advance_x = advance * usefont->size;
1492 //mapglyph->advance_x = advance;
1493 mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1494 mapglyph->advance_y = 0;
1496 if (developer_font.integer)
1498 Con_DPrintf("glyphinfo: Glyph: %lu at (%i, %i)\n", (unsigned long)ch, gC, gR);
1499 Con_DPrintf("glyphinfo: %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1500 if (ch >= 32 && ch <= 128)
1501 Con_DPrintf("glyphinfo: Character: %c\n", (int)ch);
1502 Con_DPrintf("glyphinfo: Vertex info:\n");
1503 Con_DPrintf("glyphinfo: X: ( %f -- %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1504 Con_DPrintf("glyphinfo: Y: ( %f -- %f )\n", mapglyph->vymin, mapglyph->vymax);
1505 Con_DPrintf("glyphinfo: Texture info:\n");
1506 Con_DPrintf("glyphinfo: S: ( %f -- %f )\n", mapglyph->txmin, mapglyph->txmax);
1507 Con_DPrintf("glyphinfo: T: ( %f -- %f )\n", mapglyph->tymin, mapglyph->tymax);
1508 Con_DPrintf("glyphinfo: Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1511 map->glyphs[mapch].image = false;
1514 if (map->pic->tex == r_texture_notexture)
1516 int w = map->glyphSize * FONT_CHARS_PER_LINE;
1517 int h = map->glyphSize * FONT_CHAR_LINES;
1519 // abuse the Draw_CachePic system to keep track of this texture
1520 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);
1521 // if tex is NULL for any reason, the pic->tex will remain set to r_texture_notexture
1523 map->pic->tex = tex;
1525 if (r_font_diskcache.integer >= 1)
1527 // swap to BGRA for tga writing...
1531 for (x = 0;x < s;x++)
1534 data[x*4+0] = data[x*4+2];
1537 Image_WriteTGABGRA(va("%s.tga", map_identifier), w, h, data);
1538 if (r_font_compress.integer && qglGetCompressedTexImageARB && tex)
1539 R_SaveTextureDDSFile(tex, va("dds/%s.dds", map_identifier), r_texture_dds_save.integer < 2, true);
1546 if (map->pic->tex == r_texture_notexture)
1548 // if the first try isn't successful, keep it with a broken texture
1549 // otherwise we retry to load it every single frame where ft2 rendering is used
1550 // this would be bad...
1551 // only `data' must be freed
1552 Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
1553 font->name, mapstart->size, mapidx);
1561 qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
1563 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1565 // the first map must have been loaded already
1566 if (!font->font_maps[map_index])
1568 return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
1571 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1573 while (start && start->start + FONT_CHARS_PER_MAP <= ch)
1574 start = start->next;
1575 if (start && start->start > ch)