1 /* FreeType 2 and UTF-8 encoding support for
8 #include "ft2_fontdefs.h"
10 static int img_fontmap[256] = {
11 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
12 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
13 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // shift+digit line
14 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // digits
15 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // caps
16 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // caps
17 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small
18 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small
19 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // specials
20 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // faces
21 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
22 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
23 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
24 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
25 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
26 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
30 ================================================================================
31 CVars introduced with the freetype extension
32 ================================================================================
35 cvar_t r_font_disable_freetype = {CVAR_SAVE, "r_font_disable_freetype", "1", "disable freetype support for fonts entirely"};
36 cvar_t r_font_use_alpha_textures = {CVAR_SAVE, "r_font_use_alpha_textures", "0", "use alpha-textures for font rendering, this should safe memory"};
37 cvar_t r_font_size_snapping = {CVAR_SAVE, "r_font_size_snapping", "1", "stick to good looking font sizes whenever possible - bad when the mod doesn't support it!"};
38 cvar_t r_font_kerning = {CVAR_SAVE, "r_font_kerning", "1", "Use kerning if available"};
39 cvar_t developer_font = {CVAR_SAVE, "developer_font", "0", "prints debug messages about fonts"};
42 ================================================================================
43 Function definitions. Taken from the freetype2 headers.
44 ================================================================================
49 (*qFT_Init_FreeType)( FT_Library *alibrary );
51 (*qFT_Done_FreeType)( FT_Library library );
54 (*qFT_New_Face)( FT_Library library,
55 const char* filepathname,
60 (*qFT_New_Memory_Face)( FT_Library library,
61 const FT_Byte* file_base,
66 (*qFT_Done_Face)( FT_Face face );
68 (*qFT_Select_Size)( FT_Face face,
69 FT_Int strike_index );
71 (*qFT_Request_Size)( FT_Face face,
72 FT_Size_Request req );
74 (*qFT_Set_Char_Size)( FT_Face face,
75 FT_F26Dot6 char_width,
76 FT_F26Dot6 char_height,
77 FT_UInt horz_resolution,
78 FT_UInt vert_resolution );
80 (*qFT_Set_Pixel_Sizes)( FT_Face face,
82 FT_UInt pixel_height );
84 (*qFT_Load_Glyph)( FT_Face face,
86 FT_Int32 load_flags );
88 (*qFT_Load_Char)( FT_Face face,
90 FT_Int32 load_flags );
92 (*qFT_Get_Char_Index)( FT_Face face,
95 (*qFT_Render_Glyph)( FT_GlyphSlot slot,
96 FT_Render_Mode render_mode );
98 (*qFT_Get_Kerning)( FT_Face face,
102 FT_Vector *akerning );
103 FT_EXPORT( FT_Error )
104 (*qFT_Attach_Stream)( FT_Face face,
105 FT_Open_Args* parameters );
107 ================================================================================
108 Support for dynamically loading the FreeType2 library
109 ================================================================================
112 static dllfunction_t ft2funcs[] =
114 {"FT_Init_FreeType", (void **) &qFT_Init_FreeType},
115 {"FT_Done_FreeType", (void **) &qFT_Done_FreeType},
116 //{"FT_New_Face", (void **) &qFT_New_Face},
117 {"FT_New_Memory_Face", (void **) &qFT_New_Memory_Face},
118 {"FT_Done_Face", (void **) &qFT_Done_Face},
119 {"FT_Select_Size", (void **) &qFT_Select_Size},
120 {"FT_Request_Size", (void **) &qFT_Request_Size},
121 {"FT_Set_Char_Size", (void **) &qFT_Set_Char_Size},
122 {"FT_Set_Pixel_Sizes", (void **) &qFT_Set_Pixel_Sizes},
123 {"FT_Load_Glyph", (void **) &qFT_Load_Glyph},
124 {"FT_Load_Char", (void **) &qFT_Load_Char},
125 {"FT_Get_Char_Index", (void **) &qFT_Get_Char_Index},
126 {"FT_Render_Glyph", (void **) &qFT_Render_Glyph},
127 {"FT_Get_Kerning", (void **) &qFT_Get_Kerning},
128 {"FT_Attach_Stream", (void **) &qFT_Attach_Stream},
132 /// Handle for FreeType2 DLL
133 static dllhandle_t ft2_dll = NULL;
135 /// Memory pool for fonts
136 static mempool_t *font_mempool= NULL;
137 static rtexturepool_t *font_texturepool = NULL;
139 /// FreeType library handle
140 static FT_Library font_ft2lib = NULL;
142 #define POSTPROCESS_MAXRADIUS 8
145 unsigned char *buf, *buf2;
146 int bufsize, bufwidth, bufheight, bufpitch;
147 float blur, outline, shadowx, shadowy, shadowz;
148 int padding_t, padding_b, padding_l, padding_r, blurpadding_lt, blurpadding_rb, outlinepadding_t, outlinepadding_b, outlinepadding_l, outlinepadding_r;
149 unsigned char circlematrix[2*POSTPROCESS_MAXRADIUS+1][2*POSTPROCESS_MAXRADIUS+1];
150 unsigned char gausstable[2*POSTPROCESS_MAXRADIUS+1];
153 static font_postprocess_t pp;
155 typedef struct fontfilecache_s
160 char path[MAX_QPATH];
163 #define MAX_FONTFILES 8
164 static fontfilecache_t fontfiles[MAX_FONTFILES];
165 static const unsigned char *fontfilecache_LoadFile(const char *path, qboolean quiet, fs_offset_t *filesizepointer)
170 for(i = 0; i < MAX_FONTFILES; ++i)
172 if(fontfiles[i].refcount > 0)
173 if(!strcmp(path, fontfiles[i].path))
175 *filesizepointer = fontfiles[i].len;
176 ++fontfiles[i].refcount;
177 return fontfiles[i].buf;
181 buf = FS_LoadFile(path, font_mempool, quiet, filesizepointer);
184 for(i = 0; i < MAX_FONTFILES; ++i)
185 if(fontfiles[i].refcount <= 0)
187 strlcpy(fontfiles[i].path, path, sizeof(fontfiles[i].path));
188 fontfiles[i].len = *filesizepointer;
189 fontfiles[i].buf = buf;
190 fontfiles[i].refcount = 1;
197 static void fontfilecache_Free(const unsigned char *buf)
200 for(i = 0; i < MAX_FONTFILES; ++i)
202 if(fontfiles[i].refcount > 0)
203 if(fontfiles[i].buf == buf)
205 if(--fontfiles[i].refcount <= 0)
207 Mem_Free(fontfiles[i].buf);
208 fontfiles[i].buf = NULL;
213 // if we get here, it used regular allocation
214 Mem_Free((void *) buf);
216 static void fontfilecache_FreeAll(void)
219 for(i = 0; i < MAX_FONTFILES; ++i)
221 if(fontfiles[i].refcount > 0)
222 Mem_Free(fontfiles[i].buf);
223 fontfiles[i].buf = NULL;
224 fontfiles[i].refcount = 0;
232 Unload the FreeType2 DLL
235 void Font_CloseLibrary (void)
237 fontfilecache_FreeAll();
239 Mem_FreePool(&font_mempool);
240 if (font_texturepool)
241 R_FreeTexturePool(&font_texturepool);
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");
316 font_texturepool = R_AllocTexturePool();
317 if (!font_texturepool)
319 Con_Print("ERROR: Failed to allocate FONT texture pool!\n");
325 void font_shutdown(void)
328 for (i = 0; i < dp_fonts.maxsize; ++i)
330 if (dp_fonts.f[i].ft2)
332 Font_UnloadFont(dp_fonts.f[i].ft2);
333 dp_fonts.f[i].ft2 = NULL;
339 void font_newmap(void)
345 Cvar_RegisterVariable(&r_font_disable_freetype);
346 Cvar_RegisterVariable(&r_font_use_alpha_textures);
347 Cvar_RegisterVariable(&r_font_size_snapping);
348 Cvar_RegisterVariable(&r_font_kerning);
349 Cvar_RegisterVariable(&developer_font);
351 // let's open it at startup already
356 ================================================================================
357 Implementation of a more or less lazy font loading and rendering code.
358 ================================================================================
361 #include "ft2_fontdefs.h"
363 ft2_font_t *Font_Alloc(void)
367 return (ft2_font_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_t));
370 qboolean Font_Attach(ft2_font_t *font, ft2_attachment_t *attachment)
372 ft2_attachment_t *na;
374 font->attachmentcount++;
375 na = (ft2_attachment_t*)Mem_Alloc(font_mempool, sizeof(font->attachments[0]) * font->attachmentcount);
378 if (font->attachments && font->attachmentcount > 1)
380 memcpy(na, font->attachments, sizeof(font->attachments[0]) * (font->attachmentcount - 1));
381 Mem_Free(font->attachments);
383 memcpy(na + sizeof(font->attachments[0]) * (font->attachmentcount - 1), attachment, sizeof(*attachment));
384 font->attachments = na;
388 float Font_VirtualToRealSize(float sz)
396 //vw = ((vid.width > 0) ? vid.width : vid_width.value);
397 vh = ((vid.height > 0) ? vid.height : vid_height.value);
398 // now try to scale to our actual size:
399 sn = sz * vh / vid_conheight.value;
401 if ( sn - (float)si >= 0.5 )
406 float Font_SnapTo(float val, float snapwidth)
408 return floor(val / snapwidth + 0.5f) * snapwidth;
411 static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font);
412 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only);
413 qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt)
416 ft2_font_t *ft2, *fbfont, *fb;
425 // check if a fallback font has been specified, if it has been, and the
426 // font fails to load, use the image font as main font
427 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
429 if (dpfnt->fallbacks[i][0])
433 if (!Font_LoadFile(name, dpfnt->req_face, &dpfnt->settings, ft2))
435 if (i >= MAX_FONT_FALLBACKS)
441 strlcpy(ft2->name, name, sizeof(ft2->name));
442 ft2->image_font = true;
443 ft2->has_kerning = false;
447 ft2->image_font = false;
450 // attempt to load fallback fonts:
452 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
454 if (!dpfnt->fallbacks[i][0])
456 if (! (fb = Font_Alloc()) )
458 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
462 if (!Font_LoadFile(dpfnt->fallbacks[i], dpfnt->fallback_faces[i], &dpfnt->settings, fb))
464 if(!FS_FileExists(va("%s.tga", dpfnt->fallbacks[i])))
465 if(!FS_FileExists(va("%s.png", dpfnt->fallbacks[i])))
466 if(!FS_FileExists(va("%s.jpg", dpfnt->fallbacks[i])))
467 if(!FS_FileExists(va("%s.pcx", dpfnt->fallbacks[i])))
468 Con_Printf("Failed to load font %s for fallback %i of font %s\n", dpfnt->fallbacks[i], i, name);
473 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
475 if (Font_LoadSize(fb, Font_VirtualToRealSize(dpfnt->req_sizes[s]), true))
480 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
485 // at least one size of the fallback font loaded successfully
491 if (fbfont == ft2 && ft2->image_font)
493 // no fallbacks were loaded successfully:
500 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
502 if (Font_LoadSize(ft2, Font_VirtualToRealSize(dpfnt->req_sizes[s]), false))
507 // loading failed for every requested size
508 Font_UnloadFont(ft2);
514 //Con_Printf("%i sizes loaded\n", count);
519 static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font)
522 char filename[MAX_QPATH];
525 const unsigned char *data;
526 fs_offset_t datasize;
528 memset(font, 0, sizeof(*font));
530 if (!Font_OpenLibrary())
532 if (!r_font_disable_freetype.integer)
534 Con_Printf("WARNING: can't open load font %s\n"
535 "You need the FreeType2 DLL to load font files\n",
541 font->settings = settings;
543 namelen = strlen(name);
545 // try load direct file
546 memcpy(filename, name, namelen+1);
547 data = fontfilecache_LoadFile(filename, false, &datasize);
551 memcpy(filename + namelen, ".ttf", 5);
552 data = fontfilecache_LoadFile(filename, false, &datasize);
557 memcpy(filename + namelen, ".otf", 5);
558 data = fontfilecache_LoadFile(filename, false, &datasize);
563 ft2_attachment_t afm;
565 memcpy(filename + namelen, ".pfb", 5);
566 data = fontfilecache_LoadFile(filename, false, &datasize);
570 memcpy(filename + namelen, ".afm", 5);
571 afm.data = fontfilecache_LoadFile(filename, false, &afm.size);
574 Font_Attach(font, &afm);
579 // FS_LoadFile being not-quiet should print an error :)
582 Con_DPrintf("Loading font %s face %i...\n", filename, _face);
584 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
585 if (status && _face != 0)
587 Con_Printf("Failed to load face %i of %s. Falling back to face 0\n", _face, name);
589 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, 0, (FT_Face*)&font->face);
594 Con_Printf("ERROR: can't create face for %s\n"
595 "Error %i\n", // TODO: error strings
597 Font_UnloadFont(font);
601 // add the attachments
602 for (i = 0; i < font->attachmentcount; ++i)
605 memset(&args, 0, sizeof(args));
606 args.flags = FT_OPEN_MEMORY;
607 args.memory_base = (const FT_Byte*)font->attachments[i].data;
608 args.memory_size = font->attachments[i].size;
609 if (qFT_Attach_Stream((FT_Face)font->face, &args))
610 Con_Printf("Failed to add attachment %u to %s\n", (unsigned)i, font->name);
613 memcpy(font->name, name, namelen+1);
614 font->image_font = false;
615 font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
619 void Font_Postprocess_Update(ft2_font_t *fnt, int bpp, int w, int h)
622 float gausstable[2*POSTPROCESS_MAXRADIUS+1];
623 qboolean need_gauss = (!pp.buf || pp.blur != fnt->settings->blur || pp.shadowz != fnt->settings->shadowz);
624 qboolean need_circle = (!pp.buf || pp.outline != fnt->settings->outline || pp.shadowx != fnt->settings->shadowx || pp.shadowy != fnt->settings->shadowy);
625 pp.blur = fnt->settings->blur;
626 pp.outline = fnt->settings->outline;
627 pp.shadowx = fnt->settings->shadowx;
628 pp.shadowy = fnt->settings->shadowy;
629 pp.shadowz = fnt->settings->shadowz;
630 pp.outlinepadding_l = bound(0, ceil(pp.outline - pp.shadowx), POSTPROCESS_MAXRADIUS);
631 pp.outlinepadding_r = bound(0, ceil(pp.outline + pp.shadowx), POSTPROCESS_MAXRADIUS);
632 pp.outlinepadding_t = bound(0, ceil(pp.outline - pp.shadowy), POSTPROCESS_MAXRADIUS);
633 pp.outlinepadding_b = bound(0, ceil(pp.outline + pp.shadowy), POSTPROCESS_MAXRADIUS);
634 pp.blurpadding_lt = bound(0, ceil(pp.blur - pp.shadowz), POSTPROCESS_MAXRADIUS);
635 pp.blurpadding_rb = bound(0, ceil(pp.blur + pp.shadowz), POSTPROCESS_MAXRADIUS);
636 pp.padding_l = pp.blurpadding_lt + pp.outlinepadding_l;
637 pp.padding_r = pp.blurpadding_rb + pp.outlinepadding_r;
638 pp.padding_t = pp.blurpadding_lt + pp.outlinepadding_t;
639 pp.padding_b = pp.blurpadding_rb + pp.outlinepadding_b;
643 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
644 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));
645 for(x = -pp.blurpadding_rb; x <= pp.blurpadding_lt; ++x)
646 sum += gausstable[POSTPROCESS_MAXRADIUS+x];
647 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
648 pp.gausstable[POSTPROCESS_MAXRADIUS+x] = floor(gausstable[POSTPROCESS_MAXRADIUS+x] / sum * 255 + 0.5);
652 for(y = -POSTPROCESS_MAXRADIUS; y <= POSTPROCESS_MAXRADIUS; ++y)
653 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
655 float d = pp.outline + 1 - sqrt(pow(x + pp.shadowx, 2) + pow(y + pp.shadowy, 2));
656 pp.circlematrix[POSTPROCESS_MAXRADIUS+y][POSTPROCESS_MAXRADIUS+x] = (d >= 1) ? 255 : (d <= 0) ? 0 : floor(d * 255 + 0.5);
659 pp.bufwidth = w + pp.padding_l + pp.padding_r;
660 pp.bufheight = h + pp.padding_t + pp.padding_b;
661 pp.bufpitch = pp.bufwidth;
662 needed = pp.bufwidth * pp.bufheight;
663 if(!pp.buf || pp.bufsize < needed * 2)
667 pp.bufsize = needed * 4;
668 pp.buf = (unsigned char *)Mem_Alloc(font_mempool, pp.bufsize);
669 pp.buf2 = pp.buf + needed;
673 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)
676 Font_Postprocess_Update(fnt, bpp, w, h);
681 // perform operation, not exceeding the passed padding values,
682 // but possibly reducing them
683 *pad_l = min(*pad_l, pp.padding_l);
684 *pad_r = min(*pad_r, pp.padding_r);
685 *pad_t = min(*pad_t, pp.padding_t);
686 *pad_b = min(*pad_b, pp.padding_b);
688 // calculate gauss table
690 // outline the font (RGBA only)
691 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
693 // this is like mplayer subtitle rendering
694 // bbuffer, bitmap buffer: this is our font
695 // abuffer, alpha buffer: this is pp.buf
696 // tmp: this is pp.buf2
698 // create outline buffer
699 memset(pp.buf, 0, pp.bufwidth * pp.bufheight);
700 for(y = -*pad_t; y < h + *pad_b; ++y)
701 for(x = -*pad_l; x < w + *pad_r; ++x)
703 int x1 = max(-x, -pp.outlinepadding_r);
704 int y1 = max(-y, -pp.outlinepadding_b);
705 int x2 = min(pp.outlinepadding_l, w-1-x);
706 int y2 = min(pp.outlinepadding_t, h-1-y);
710 for(my = y1; my <= y2; ++my)
711 for(mx = x1; mx <= x2; ++mx)
713 cur = pp.circlematrix[POSTPROCESS_MAXRADIUS+my][POSTPROCESS_MAXRADIUS+mx] * (int)imagedata[(x+mx) * bpp + pitch * (y+my) + (bpp - 1)];
717 pp.buf[((x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t))] = (highest + 128) / 255;
720 // blur the outline buffer
721 if(pp.blur > 0 || pp.shadowz != 0)
724 for(y = 0; y < pp.bufheight; ++y)
725 for(x = 0; x < pp.bufwidth; ++x)
727 int x1 = max(-x, -pp.blurpadding_rb);
728 int x2 = min(pp.blurpadding_lt, pp.bufwidth-1-x);
731 for(mx = x1; mx <= x2; ++mx)
732 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+mx] * (int)pp.buf[(x+mx) + pp.bufpitch * y];
733 pp.buf2[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
737 for(y = 0; y < pp.bufheight; ++y)
738 for(x = 0; x < pp.bufwidth; ++x)
740 int y1 = max(-y, -pp.blurpadding_rb);
741 int y2 = min(pp.blurpadding_lt, pp.bufheight-1-y);
744 for(my = y1; my <= y2; ++my)
745 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+my] * (int)pp.buf2[x + pp.bufpitch * (y+my)];
746 pp.buf[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
750 // paste the outline below the font
751 for(y = -*pad_t; y < h + *pad_b; ++y)
752 for(x = -*pad_l; x < w + *pad_r; ++x)
754 unsigned char outlinealpha = pp.buf[(x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t)];
757 unsigned char oldalpha = imagedata[x * bpp + pitch * y + (bpp - 1)];
758 // a' = 1 - (1 - a1) (1 - a2)
759 unsigned char newalpha = 255 - ((255 - (int)outlinealpha) * (255 - (int)oldalpha)) / 255; // this is >= oldalpha
760 // c' = (a2 c2 - a1 a2 c1 + a1 c1) / a' = (a2 c2 + a1 (1 - a2) c1) / a'
761 unsigned char oldfactor = (255 * (int)oldalpha) / newalpha;
762 //unsigned char outlinefactor = ((255 - oldalpha) * (int)outlinealpha) / newalpha;
764 for(i = 0; i < bpp-1; ++i)
766 unsigned char c = imagedata[x * bpp + pitch * y + i];
767 c = (c * (int)oldfactor) / 255 /* + outlinecolor[i] * (int)outlinefactor */;
768 imagedata[x * bpp + pitch * y + i] = c;
770 imagedata[x * bpp + pitch * y + (bpp - 1)] = newalpha;
772 //imagedata[x * bpp + pitch * y + (bpp - 1)] |= 0x80;
778 // just calculate parameters
779 *pad_l = pp.padding_l;
780 *pad_r = pp.padding_r;
781 *pad_t = pp.padding_t;
782 *pad_b = pp.padding_b;
786 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size);
787 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
788 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only)
791 ft2_font_map_t *fmap, temp;
792 int gpad_l, gpad_r, gpad_t, gpad_b;
794 if (!(size > 0.001f && size < 1000.0f))
799 if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
802 for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
804 if (!font->font_maps[map_index])
806 // if a similar size has already been loaded, ignore this one
807 //abs(font->font_maps[map_index]->size - size) < 4
808 if (font->font_maps[map_index]->size == size)
812 if (map_index >= MAX_FONT_SIZES)
817 if (font->image_font)
818 fontface = (FT_Face)font->next->face;
820 fontface = (FT_Face)font->face;
821 return (Font_SearchSize(font, fontface, size) > 0);
824 Font_Postprocess(font, NULL, 0, 4, size*2, size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
826 memset(&temp, 0, sizeof(temp));
828 temp.glyphSize = CeilPowerOf2(size*2 + max(gpad_l + gpad_r, gpad_t + gpad_b));
829 temp.sfx = (1.0/64.0)/(double)size;
830 temp.sfy = (1.0/64.0)/(double)size;
831 temp.intSize = -1; // negative value: LoadMap must search now :)
832 if (!Font_LoadMap(font, &temp, 0, &fmap))
834 Con_Printf("ERROR: can't load the first character map for %s\n"
837 Font_UnloadFont(font);
840 font->font_maps[map_index] = temp.next;
842 fmap->sfx = temp.sfx;
843 fmap->sfy = temp.sfy;
845 // load the default kerning vector:
846 if (font->has_kerning)
850 for (l = 0; l < 256; ++l)
852 for (r = 0; r < 256; ++r)
855 ul = qFT_Get_Char_Index((FT_Face)font->face, l);
856 ur = qFT_Get_Char_Index((FT_Face)font->face, r);
857 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
859 fmap->kerning.kerning[l][r][0] = 0;
860 fmap->kerning.kerning[l][r][1] = 0;
864 fmap->kerning.kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
865 fmap->kerning.kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
873 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
876 float value = 1000000;
878 int matchsize = -10000;
880 float fsize_x, fsize_y;
881 ft2_font_map_t **maps = font->font_maps;
883 fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value;
885 fsize_x = *outw * vid.width / vid_conwidth.value;
887 fsize_y = *outh * vid.height / vid_conheight.value;
892 fsize_x = fsize_y = 16;
902 for (m = 0; m < MAX_FONT_SIZES; ++m)
906 // "round up" to the bigger size if two equally-valued matches exist
907 nval = 0.5 * (fabs(maps[m]->size - fsize_x) + fabs(maps[m]->size - fsize_y));
908 if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
912 matchsize = maps[m]->size;
913 if (value == 0) // there is no better match
917 if (value <= r_font_size_snapping.value)
919 // do NOT keep the aspect for perfect rendering
920 if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
921 if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width;
926 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
928 if (index < 0 || index >= MAX_FONT_SIZES)
930 return font->font_maps[index];
933 static qboolean Font_SetSize(ft2_font_t *font, float w, float h)
935 if (font->currenth == h &&
936 ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
937 font->currentw == w)) // same size has been requested
941 // sorry, but freetype doesn't seem to care about other sizes
944 if (font->image_font)
946 if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
951 if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
959 qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
961 ft2_font_map_t *fmap;
962 if (!font->has_kerning || !r_font_kerning.integer)
964 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
966 fmap = font->font_maps[map_index];
969 if (left < 256 && right < 256)
971 //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
972 // quick-kerning, be aware of the size: scale it
973 if (outx) *outx = fmap->kerning.kerning[left][right][0];// * (w / (float)fmap->size);
974 if (outy) *outy = fmap->kerning.kerning[left][right][1];// * (h / (float)fmap->size);
982 //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
984 if (!Font_SetSize(font, w, h))
986 // this deserves an error message
987 Con_Printf("Failed to get kerning for %s\n", font->name);
990 ul = qFT_Get_Char_Index(font->face, left);
991 ur = qFT_Get_Char_Index(font->face, right);
992 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
994 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);
995 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);
999 if (!Font_SetSize(font, fmap->intSize, fmap->intSize))
1001 // this deserves an error message
1002 Con_Printf("Failed to get kerning for %s\n", font->name);
1005 ul = qFT_Get_Char_Index((FT_Face)font->face, left);
1006 ur = qFT_Get_Char_Index((FT_Face)font->face, right);
1007 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
1009 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);// * (w / (float)fmap->size);
1010 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);// * (h / (float)fmap->size);
1017 qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
1019 return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
1022 static void UnloadMapRec(ft2_font_map_t *map)
1026 R_FreeTexture(map->texture);
1027 map->texture = NULL;
1030 UnloadMapRec(map->next);
1034 void Font_UnloadFont(ft2_font_t *font)
1040 Font_UnloadFont(font->next);
1042 if (font->attachments && font->attachmentcount)
1044 for (i = 0; i < (int)font->attachmentcount; ++i) {
1045 if (font->attachments[i].data)
1046 fontfilecache_Free(font->attachments[i].data);
1048 Mem_Free(font->attachments);
1049 font->attachmentcount = 0;
1050 font->attachments = NULL;
1052 for (i = 0; i < MAX_FONT_SIZES; ++i)
1054 if (font->font_maps[i])
1056 UnloadMapRec(font->font_maps[i]);
1057 font->font_maps[i] = NULL;
1064 qFT_Done_Face((FT_Face)font->face);
1069 fontfilecache_Free(font->data);
1074 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size)
1076 float intSize = size;
1079 if (!Font_SetSize(font, intSize, intSize))
1081 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, size, intSize);
1084 if ((fontface->size->metrics.height>>6) <= size)
1088 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, size);
1095 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
1097 char map_identifier[MAX_QPATH];
1098 unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
1099 unsigned char *data;
1103 FT_Int32 load_flags;
1104 int gpad_l, gpad_r, gpad_t, gpad_b;
1107 int gR, gC; // glyph position: row and column
1109 ft2_font_map_t *map, *next;
1110 ft2_font_t *usefont;
1114 int bytesPerPixel = 4; // change the conversion loop too if you change this!
1119 if (r_font_use_alpha_textures.integer)
1122 if (font->image_font)
1123 fontface = (FT_Face)font->next->face;
1125 fontface = (FT_Face)font->face;
1127 switch(font->settings->antialias)
1130 switch(font->settings->hinting)
1133 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1137 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1141 load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1147 switch(font->settings->hinting)
1150 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1153 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
1156 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1160 load_flags = FT_LOAD_TARGET_NORMAL;
1166 //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
1168 if (font->image_font && mapstart->intSize < 0)
1169 mapstart->intSize = mapstart->size;
1170 if (mapstart->intSize < 0)
1173 mapstart->intSize = mapstart->size;
1176 if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1178 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
1181 if ((fontface->size->metrics.height>>6) <= mapstart->size)
1183 if (mapstart->intSize < 2)
1185 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
1188 --mapstart->intSize;
1191 if ((mapstart->intSize = Font_SearchSize(font, fontface, mapstart->size)) <= 0)
1193 Con_DPrintf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
1196 if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1198 Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
1202 map = (ft2_font_map_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
1205 Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
1209 Font_Postprocess(font, NULL, 0, bytesPerPixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
1211 // copy over the information
1212 map->size = mapstart->size;
1213 map->intSize = mapstart->intSize;
1214 map->glyphSize = mapstart->glyphSize;
1215 map->sfx = mapstart->sfx;
1216 map->sfy = mapstart->sfy;
1218 pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
1219 data = (unsigned char *)Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
1222 Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
1226 memset(map->width_of, 0, sizeof(map->width_of));
1228 // initialize as white texture with zero alpha
1230 while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
1232 if (bytesPerPixel == 4)
1242 map->start = mapidx * FONT_CHARS_PER_MAP;
1244 while(next->next && next->next->start < map->start)
1246 map->next = next->next;
1251 for (ch = map->start;
1252 ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
1255 FT_ULong glyphIndex;
1259 unsigned char *imagedata, *dst, *src;
1260 glyph_slot_t *mapglyph;
1262 int pad_l, pad_r, pad_t, pad_b;
1264 mapch = ch - map->start;
1266 if (developer_font.integer)
1267 Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
1270 if (gC >= FONT_CHARS_PER_LINE)
1272 gC -= FONT_CHARS_PER_LINE;
1276 imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
1277 imagedata += gpad_t * pitch + gpad_l * bytesPerPixel;
1278 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
1279 // we need the glyphIndex
1280 face = (FT_Face)font->face;
1282 if (font->image_font && mapch == ch && img_fontmap[mapch])
1284 map->glyphs[mapch].image = true;
1287 glyphIndex = qFT_Get_Char_Index(face, ch);
1288 if (glyphIndex == 0)
1290 // by convention, 0 is the "missing-glyph"-glyph
1291 // try to load from a fallback font
1292 for(usefont = font->next; usefont != NULL; usefont = usefont->next)
1294 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
1297 face = (FT_Face)usefont->face;
1298 glyphIndex = qFT_Get_Char_Index(face, ch);
1299 if (glyphIndex == 0)
1301 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1308 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1309 // now we let it use the "missing-glyph"-glyph
1310 face = (FT_Face)font->face;
1318 face = (FT_Face)font->face;
1319 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1322 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1323 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1328 glyph = face->glyph;
1329 bmp = &glyph->bitmap;
1334 if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
1335 Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
1336 if (w > map->glyphSize)
1337 w = map->glyphSize - gpad_l - gpad_r;
1338 if (h > map->glyphSize)
1342 switch (bmp->pixel_mode)
1344 case FT_PIXEL_MODE_MONO:
1345 if (developer_font.integer)
1346 Con_DPrint("glyphinfo: Pixel Mode: MONO\n");
1348 case FT_PIXEL_MODE_GRAY2:
1349 if (developer_font.integer)
1350 Con_DPrint("glyphinfo: Pixel Mode: GRAY2\n");
1352 case FT_PIXEL_MODE_GRAY4:
1353 if (developer_font.integer)
1354 Con_DPrint("glyphinfo: Pixel Mode: GRAY4\n");
1356 case FT_PIXEL_MODE_GRAY:
1357 if (developer_font.integer)
1358 Con_DPrint("glyphinfo: Pixel Mode: GRAY\n");
1361 if (developer_font.integer)
1362 Con_DPrintf("glyphinfo: Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1364 Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1367 for (y = 0; y < h; ++y)
1369 dst = imagedata + y * pitch;
1370 src = bmp->buffer + y * bmp->pitch;
1372 switch (bmp->pixel_mode)
1374 case FT_PIXEL_MODE_MONO:
1375 dst += bytesPerPixel - 1; // shift to alpha byte
1376 for (x = 0; x < bmp->width; x += 8)
1378 unsigned char ch = *src++;
1379 *dst = 255 * !!((ch & 0x80) >> 7); dst += bytesPerPixel;
1380 *dst = 255 * !!((ch & 0x40) >> 6); dst += bytesPerPixel;
1381 *dst = 255 * !!((ch & 0x20) >> 5); dst += bytesPerPixel;
1382 *dst = 255 * !!((ch & 0x10) >> 4); dst += bytesPerPixel;
1383 *dst = 255 * !!((ch & 0x08) >> 3); dst += bytesPerPixel;
1384 *dst = 255 * !!((ch & 0x04) >> 2); dst += bytesPerPixel;
1385 *dst = 255 * !!((ch & 0x02) >> 1); dst += bytesPerPixel;
1386 *dst = 255 * !!((ch & 0x01) >> 0); dst += bytesPerPixel;
1389 case FT_PIXEL_MODE_GRAY2:
1390 dst += bytesPerPixel - 1; // shift to alpha byte
1391 for (x = 0; x < bmp->width; x += 4)
1393 unsigned char ch = *src++;
1394 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1395 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1396 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1397 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1400 case FT_PIXEL_MODE_GRAY4:
1401 dst += bytesPerPixel - 1; // shift to alpha byte
1402 for (x = 0; x < bmp->width; x += 2)
1404 unsigned char ch = *src++;
1405 *dst = ( ((ch & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
1406 *dst = ( ((ch & 0x0F) ) * 0x11); dst += bytesPerPixel;
1409 case FT_PIXEL_MODE_GRAY:
1410 // in this case pitch should equal width
1411 for (tp = 0; tp < bmp->pitch; ++tp)
1412 dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
1414 //memcpy((void*)dst, (void*)src, bmp->pitch);
1415 //dst += bmp->pitch;
1426 Font_Postprocess(font, imagedata, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1428 // now fill map->glyphs[ch - map->start]
1429 mapglyph = &map->glyphs[mapch];
1433 // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1435 double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1436 //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1437 double advance = (glyph->advance.x / 64.0) / map->size;
1438 //double mWidth = (glyph->metrics.width >> 6) / map->size;
1439 //double mHeight = (glyph->metrics.height >> 6) / map->size;
1441 mapglyph->txmin = ( (double)(gC * map->glyphSize) + (double)(gpad_l - pad_l) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1442 mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1443 mapglyph->tymin = ( (double)(gR * map->glyphSize) + (double)(gpad_r - pad_r) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1444 mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1445 //mapglyph->vxmin = bearingX;
1446 //mapglyph->vxmax = bearingX + mWidth;
1447 mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
1448 mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
1449 //mapglyph->vymin = -bearingY;
1450 //mapglyph->vymax = mHeight - bearingY;
1451 mapglyph->vymin = (-glyph->bitmap_top - pad_t) / map->size;
1452 mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
1453 //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);
1454 //mapglyph->advance_x = advance * usefont->size;
1455 //mapglyph->advance_x = advance;
1456 mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1457 mapglyph->advance_y = 0;
1459 if (developer_font.integer)
1461 Con_DPrintf("glyphinfo: Glyph: %lu at (%i, %i)\n", (unsigned long)ch, gC, gR);
1462 Con_DPrintf("glyphinfo: %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1463 if (ch >= 32 && ch <= 128)
1464 Con_DPrintf("glyphinfo: Character: %c\n", (int)ch);
1465 Con_DPrintf("glyphinfo: Vertex info:\n");
1466 Con_DPrintf("glyphinfo: X: ( %f -- %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1467 Con_DPrintf("glyphinfo: Y: ( %f -- %f )\n", mapglyph->vymin, mapglyph->vymax);
1468 Con_DPrintf("glyphinfo: Texture info:\n");
1469 Con_DPrintf("glyphinfo: S: ( %f -- %f )\n", mapglyph->txmin, mapglyph->txmax);
1470 Con_DPrintf("glyphinfo: T: ( %f -- %f )\n", mapglyph->tymin, mapglyph->tymax);
1471 Con_DPrintf("glyphinfo: Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1474 map->glyphs[mapch].image = false;
1477 // create a texture from the data now
1479 if (developer_font.integer > 100)
1481 // LordHavoc: why are we writing this? And why not write it as TGA using the appropriate function?
1482 // view using `display -depth 8 -size 512x512 name_page.rgba` (be sure to use a correct -size parameter)
1483 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u.rgba", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1484 FS_WriteFile(map_identifier, data, pitch * FONT_CHAR_LINES * map->glyphSize);
1486 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1488 // probably use bytesPerPixel here instead?
1489 if (r_font_use_alpha_textures.integer)
1491 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1492 map->glyphSize * FONT_CHARS_PER_LINE,
1493 map->glyphSize * FONT_CHAR_LINES,
1494 data, TEXTYPE_ALPHA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, -1, NULL);
1496 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1497 map->glyphSize * FONT_CHARS_PER_LINE,
1498 map->glyphSize * FONT_CHAR_LINES,
1499 data, TEXTYPE_RGBA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, -1, NULL);
1505 // if the first try isn't successful, keep it with a broken texture
1506 // otherwise we retry to load it every single frame where ft2 rendering is used
1507 // this would be bad...
1508 // only `data' must be freed
1509 Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
1510 font->name, mapstart->size, mapidx);
1518 qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
1520 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1522 // the first map must have been loaded already
1523 if (!font->font_maps[map_index])
1525 return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
1528 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1530 while (start && start->start + FONT_CHARS_PER_MAP <= ch)
1531 start = start->next;
1532 if (start && start->start > ch)