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",
1216 (double) mapstart->intSize,
1218 (double) font->settings->blur,
1219 (double) font->settings->outline,
1220 (double) font->settings->shadowx,
1221 (double) font->settings->shadowy,
1222 (double) font->settings->shadowz,
1225 // create a cachepic_t from the data now, or reuse an existing one
1226 map->pic = Draw_CachePic_Flags(map_identifier, CACHEPICFLAG_QUIET);
1227 if (developer_font.integer)
1229 if (map->pic->tex == r_texture_notexture)
1230 Con_Printf("Generating font map %s (size: %.1f MB)\n", map_identifier, mapstart->glyphSize * (256 * 4 / 1048576.0) * mapstart->glyphSize);
1232 Con_Printf("Using cached font map %s (size: %.1f MB)\n", map_identifier, mapstart->glyphSize * (256 * 4 / 1048576.0) * mapstart->glyphSize);
1235 Font_Postprocess(font, NULL, 0, bytesPerPixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
1237 // copy over the information
1238 map->size = mapstart->size;
1239 map->intSize = mapstart->intSize;
1240 map->glyphSize = mapstart->glyphSize;
1241 map->sfx = mapstart->sfx;
1242 map->sfy = mapstart->sfy;
1244 pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
1245 if (map->pic->tex == r_texture_notexture)
1247 data = (unsigned char *)Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
1250 Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
1254 // initialize as white texture with zero alpha
1256 while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
1258 if (bytesPerPixel == 4)
1268 memset(map->width_of, 0, sizeof(map->width_of));
1271 map->start = mapidx * FONT_CHARS_PER_MAP;
1273 while(next->next && next->next->start < map->start)
1275 map->next = next->next;
1280 for (ch = map->start;
1281 ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
1284 FT_ULong glyphIndex;
1288 unsigned char *imagedata = NULL, *dst, *src;
1289 glyph_slot_t *mapglyph;
1291 int pad_l, pad_r, pad_t, pad_b;
1293 mapch = ch - map->start;
1295 if (developer_font.integer)
1296 Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
1299 if (gC >= FONT_CHARS_PER_LINE)
1301 gC -= FONT_CHARS_PER_LINE;
1307 imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
1308 imagedata += gpad_t * pitch + gpad_l * bytesPerPixel;
1310 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
1311 // we need the glyphIndex
1312 face = (FT_Face)font->face;
1314 if (font->image_font && mapch == ch && img_fontmap[mapch])
1316 map->glyphs[mapch].image = true;
1319 glyphIndex = qFT_Get_Char_Index(face, ch);
1320 if (glyphIndex == 0)
1322 // by convention, 0 is the "missing-glyph"-glyph
1323 // try to load from a fallback font
1324 for(usefont = font->next; usefont != NULL; usefont = usefont->next)
1326 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
1329 face = (FT_Face)usefont->face;
1330 glyphIndex = qFT_Get_Char_Index(face, ch);
1331 if (glyphIndex == 0)
1333 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1340 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1341 // now we let it use the "missing-glyph"-glyph
1342 face = (FT_Face)font->face;
1350 face = (FT_Face)font->face;
1351 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1354 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1355 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1360 glyph = face->glyph;
1361 bmp = &glyph->bitmap;
1366 if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
1367 Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
1368 if (w > map->glyphSize)
1369 w = map->glyphSize - gpad_l - gpad_r;
1370 if (h > map->glyphSize)
1376 switch (bmp->pixel_mode)
1378 case FT_PIXEL_MODE_MONO:
1379 if (developer_font.integer)
1380 Con_DPrint("glyphinfo: Pixel Mode: MONO\n");
1382 case FT_PIXEL_MODE_GRAY2:
1383 if (developer_font.integer)
1384 Con_DPrint("glyphinfo: Pixel Mode: GRAY2\n");
1386 case FT_PIXEL_MODE_GRAY4:
1387 if (developer_font.integer)
1388 Con_DPrint("glyphinfo: Pixel Mode: GRAY4\n");
1390 case FT_PIXEL_MODE_GRAY:
1391 if (developer_font.integer)
1392 Con_DPrint("glyphinfo: Pixel Mode: GRAY\n");
1395 if (developer_font.integer)
1396 Con_DPrintf("glyphinfo: Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1398 Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1401 for (y = 0; y < h; ++y)
1403 dst = imagedata + y * pitch;
1404 src = bmp->buffer + y * bmp->pitch;
1406 switch (bmp->pixel_mode)
1408 case FT_PIXEL_MODE_MONO:
1409 dst += bytesPerPixel - 1; // shift to alpha byte
1410 for (x = 0; x < bmp->width; x += 8)
1412 unsigned char ch = *src++;
1413 *dst = 255 * !!((ch & 0x80) >> 7); dst += bytesPerPixel;
1414 *dst = 255 * !!((ch & 0x40) >> 6); dst += bytesPerPixel;
1415 *dst = 255 * !!((ch & 0x20) >> 5); dst += bytesPerPixel;
1416 *dst = 255 * !!((ch & 0x10) >> 4); dst += bytesPerPixel;
1417 *dst = 255 * !!((ch & 0x08) >> 3); dst += bytesPerPixel;
1418 *dst = 255 * !!((ch & 0x04) >> 2); dst += bytesPerPixel;
1419 *dst = 255 * !!((ch & 0x02) >> 1); dst += bytesPerPixel;
1420 *dst = 255 * !!((ch & 0x01) >> 0); dst += bytesPerPixel;
1423 case FT_PIXEL_MODE_GRAY2:
1424 dst += bytesPerPixel - 1; // shift to alpha byte
1425 for (x = 0; x < bmp->width; x += 4)
1427 unsigned char ch = *src++;
1428 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1429 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1430 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1431 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1434 case FT_PIXEL_MODE_GRAY4:
1435 dst += bytesPerPixel - 1; // shift to alpha byte
1436 for (x = 0; x < bmp->width; x += 2)
1438 unsigned char ch = *src++;
1439 *dst = ( ((ch & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
1440 *dst = ( ((ch & 0x0F) ) * 0x11); dst += bytesPerPixel;
1443 case FT_PIXEL_MODE_GRAY:
1444 // in this case pitch should equal width
1445 for (tp = 0; tp < bmp->pitch; ++tp)
1446 dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
1448 //memcpy((void*)dst, (void*)src, bmp->pitch);
1449 //dst += bmp->pitch;
1460 Font_Postprocess(font, imagedata, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1468 Font_Postprocess(font, NULL, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1472 // now fill map->glyphs[ch - map->start]
1473 mapglyph = &map->glyphs[mapch];
1477 // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1479 double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1480 //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1481 double advance = (glyph->advance.x / 64.0) / map->size;
1482 //double mWidth = (glyph->metrics.width >> 6) / map->size;
1483 //double mHeight = (glyph->metrics.height >> 6) / map->size;
1485 mapglyph->txmin = ( (double)(gC * map->glyphSize) + (double)(gpad_l - pad_l) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1486 mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1487 mapglyph->tymin = ( (double)(gR * map->glyphSize) + (double)(gpad_r - pad_r) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1488 mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1489 //mapglyph->vxmin = bearingX;
1490 //mapglyph->vxmax = bearingX + mWidth;
1491 mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
1492 mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
1493 //mapglyph->vymin = -bearingY;
1494 //mapglyph->vymax = mHeight - bearingY;
1495 mapglyph->vymin = (-glyph->bitmap_top - pad_t) / map->size;
1496 mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
1497 //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);
1498 //mapglyph->advance_x = advance * usefont->size;
1499 //mapglyph->advance_x = advance;
1500 mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1501 mapglyph->advance_y = 0;
1503 if (developer_font.integer)
1505 Con_DPrintf("glyphinfo: Glyph: %lu at (%i, %i)\n", (unsigned long)ch, gC, gR);
1506 Con_DPrintf("glyphinfo: %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1507 if (ch >= 32 && ch <= 128)
1508 Con_DPrintf("glyphinfo: Character: %c\n", (int)ch);
1509 Con_DPrintf("glyphinfo: Vertex info:\n");
1510 Con_DPrintf("glyphinfo: X: ( %f -- %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1511 Con_DPrintf("glyphinfo: Y: ( %f -- %f )\n", mapglyph->vymin, mapglyph->vymax);
1512 Con_DPrintf("glyphinfo: Texture info:\n");
1513 Con_DPrintf("glyphinfo: S: ( %f -- %f )\n", mapglyph->txmin, mapglyph->txmax);
1514 Con_DPrintf("glyphinfo: T: ( %f -- %f )\n", mapglyph->tymin, mapglyph->tymax);
1515 Con_DPrintf("glyphinfo: Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1518 map->glyphs[mapch].image = false;
1521 if (map->pic->tex == r_texture_notexture)
1523 int w = map->glyphSize * FONT_CHARS_PER_LINE;
1524 int h = map->glyphSize * FONT_CHAR_LINES;
1526 // abuse the Draw_CachePic system to keep track of this texture
1527 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);
1528 // if tex is NULL for any reason, the pic->tex will remain set to r_texture_notexture
1530 map->pic->tex = tex;
1532 if (r_font_diskcache.integer >= 1)
1534 // swap to BGRA for tga writing...
1538 for (x = 0;x < s;x++)
1541 data[x*4+0] = data[x*4+2];
1544 Image_WriteTGABGRA(va("%s.tga", map_identifier), w, h, data);
1545 if (r_font_compress.integer && qglGetCompressedTexImageARB && tex)
1546 R_SaveTextureDDSFile(tex, va("dds/%s.dds", map_identifier), r_texture_dds_save.integer < 2, true);
1553 if (map->pic->tex == r_texture_notexture)
1555 // if the first try isn't successful, keep it with a broken texture
1556 // otherwise we retry to load it every single frame where ft2 rendering is used
1557 // this would be bad...
1558 // only `data' must be freed
1559 Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
1560 font->name, mapstart->size, mapidx);
1568 qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
1570 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1572 // the first map must have been loaded already
1573 if (!font->font_maps[map_index])
1575 return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
1578 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1580 while (start && start->start + FONT_CHARS_PER_MAP <= ch)
1581 start = start->next;
1582 if (start && start->start > ch)