1 /* FreeType 2 and UTF-8 encoding support for
8 #include "ft2_fontdefs.h"
10 static int img_fontmap[256] = {
11 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
12 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
13 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // shift+digit line
14 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // digits
15 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // caps
16 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // caps
17 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small
18 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small
19 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // specials
20 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // faces
21 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
22 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
23 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
24 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
25 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
26 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
30 ================================================================================
31 CVars introduced with the freetype extension
32 ================================================================================
35 cvar_t r_font_disable_freetype = {CVAR_SAVE, "r_font_disable_freetype", "1", "disable freetype support for fonts entirely"};
36 cvar_t r_font_use_alpha_textures = {CVAR_SAVE, "r_font_use_alpha_textures", "0", "use alpha-textures for font rendering, this should safe memory"};
37 cvar_t r_font_size_snapping = {CVAR_SAVE, "r_font_size_snapping", "1", "stick to good looking font sizes whenever possible - bad when the mod doesn't support it!"};
38 cvar_t r_font_kerning = {CVAR_SAVE, "r_font_kerning", "1", "Use kerning if available"};
39 cvar_t developer_font = {CVAR_SAVE, "developer_font", "0", "prints debug messages about fonts"};
42 ================================================================================
43 Function definitions. Taken from the freetype2 headers.
44 ================================================================================
49 (*qFT_Init_FreeType)( FT_Library *alibrary );
51 (*qFT_Done_FreeType)( FT_Library library );
54 (*qFT_New_Face)( FT_Library library,
55 const char* filepathname,
60 (*qFT_New_Memory_Face)( FT_Library library,
61 const FT_Byte* file_base,
66 (*qFT_Done_Face)( FT_Face face );
68 (*qFT_Select_Size)( FT_Face face,
69 FT_Int strike_index );
71 (*qFT_Request_Size)( FT_Face face,
72 FT_Size_Request req );
74 (*qFT_Set_Char_Size)( FT_Face face,
75 FT_F26Dot6 char_width,
76 FT_F26Dot6 char_height,
77 FT_UInt horz_resolution,
78 FT_UInt vert_resolution );
80 (*qFT_Set_Pixel_Sizes)( FT_Face face,
82 FT_UInt pixel_height );
84 (*qFT_Load_Glyph)( FT_Face face,
86 FT_Int32 load_flags );
88 (*qFT_Load_Char)( FT_Face face,
90 FT_Int32 load_flags );
92 (*qFT_Get_Char_Index)( FT_Face face,
95 (*qFT_Render_Glyph)( FT_GlyphSlot slot,
96 FT_Render_Mode render_mode );
98 (*qFT_Get_Kerning)( FT_Face face,
102 FT_Vector *akerning );
103 FT_EXPORT( FT_Error )
104 (*qFT_Attach_Stream)( FT_Face face,
105 FT_Open_Args* parameters );
107 ================================================================================
108 Support for dynamically loading the FreeType2 library
109 ================================================================================
112 static dllfunction_t ft2funcs[] =
114 {"FT_Init_FreeType", (void **) &qFT_Init_FreeType},
115 {"FT_Done_FreeType", (void **) &qFT_Done_FreeType},
116 //{"FT_New_Face", (void **) &qFT_New_Face},
117 {"FT_New_Memory_Face", (void **) &qFT_New_Memory_Face},
118 {"FT_Done_Face", (void **) &qFT_Done_Face},
119 {"FT_Select_Size", (void **) &qFT_Select_Size},
120 {"FT_Request_Size", (void **) &qFT_Request_Size},
121 {"FT_Set_Char_Size", (void **) &qFT_Set_Char_Size},
122 {"FT_Set_Pixel_Sizes", (void **) &qFT_Set_Pixel_Sizes},
123 {"FT_Load_Glyph", (void **) &qFT_Load_Glyph},
124 {"FT_Load_Char", (void **) &qFT_Load_Char},
125 {"FT_Get_Char_Index", (void **) &qFT_Get_Char_Index},
126 {"FT_Render_Glyph", (void **) &qFT_Render_Glyph},
127 {"FT_Get_Kerning", (void **) &qFT_Get_Kerning},
128 {"FT_Attach_Stream", (void **) &qFT_Attach_Stream},
132 /// Handle for FreeType2 DLL
133 static dllhandle_t ft2_dll = NULL;
135 /// Memory pool for fonts
136 static mempool_t *font_mempool= NULL;
137 static rtexturepool_t *font_texturepool = NULL;
139 /// FreeType library handle
140 static FT_Library font_ft2lib = NULL;
142 #define POSTPROCESS_MAXRADIUS 8
145 unsigned char *buf, *buf2;
146 int bufsize, bufwidth, bufheight, bufpitch;
147 float blur, outline, shadowx, shadowy, shadowz;
148 int padding_t, padding_b, padding_l, padding_r, blurpadding_lt, blurpadding_rb, outlinepadding_t, outlinepadding_b, outlinepadding_l, outlinepadding_r;
149 unsigned char circlematrix[2*POSTPROCESS_MAXRADIUS+1][2*POSTPROCESS_MAXRADIUS+1];
150 unsigned char gausstable[2*POSTPROCESS_MAXRADIUS+1];
153 static font_postprocess_t pp;
159 Unload the FreeType2 DLL
162 void Font_CloseLibrary (void)
165 Mem_FreePool(&font_mempool);
166 if (font_texturepool)
167 R_FreeTexturePool(&font_texturepool);
168 if (font_ft2lib && qFT_Done_FreeType)
170 qFT_Done_FreeType(font_ft2lib);
173 Sys_UnloadLibrary (&ft2_dll);
181 Try to load the FreeType2 DLL
184 qboolean Font_OpenLibrary (void)
186 const char* dllnames [] =
191 #elif defined(MACOSX)
192 "libfreetype.6.dylib",
201 if (r_font_disable_freetype.integer)
209 if (!Sys_LoadLibrary (dllnames, &ft2_dll, ft2funcs))
218 Initialize the freetype2 font subsystem
222 void font_start(void)
224 if (!Font_OpenLibrary())
227 if (qFT_Init_FreeType(&font_ft2lib))
229 Con_Print("ERROR: Failed to initialize the FreeType2 library!\n");
234 font_mempool = Mem_AllocPool("FONT", 0, NULL);
237 Con_Print("ERROR: Failed to allocate FONT memory pool!\n");
242 font_texturepool = R_AllocTexturePool();
243 if (!font_texturepool)
245 Con_Print("ERROR: Failed to allocate FONT texture pool!\n");
251 void font_shutdown(void)
254 for (i = 0; i < dp_fonts.maxsize; ++i)
256 if (dp_fonts.f[i].ft2)
258 Font_UnloadFont(dp_fonts.f[i].ft2);
259 dp_fonts.f[i].ft2 = NULL;
265 void font_newmap(void)
271 Cvar_RegisterVariable(&r_font_disable_freetype);
272 Cvar_RegisterVariable(&r_font_use_alpha_textures);
273 Cvar_RegisterVariable(&r_font_size_snapping);
274 Cvar_RegisterVariable(&r_font_kerning);
275 Cvar_RegisterVariable(&developer_font);
277 // let's open it at startup already
282 ================================================================================
283 Implementation of a more or less lazy font loading and rendering code.
284 ================================================================================
287 #include "ft2_fontdefs.h"
289 ft2_font_t *Font_Alloc(void)
293 return (ft2_font_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_t));
296 qboolean Font_Attach(ft2_font_t *font, ft2_attachment_t *attachment)
298 ft2_attachment_t *na;
300 font->attachmentcount++;
301 na = (ft2_attachment_t*)Mem_Alloc(font_mempool, sizeof(font->attachments[0]) * font->attachmentcount);
304 if (font->attachments && font->attachmentcount > 1)
306 memcpy(na, font->attachments, sizeof(font->attachments[0]) * (font->attachmentcount - 1));
307 Mem_Free(font->attachments);
309 memcpy(na + sizeof(font->attachments[0]) * (font->attachmentcount - 1), attachment, sizeof(*attachment));
310 font->attachments = na;
314 float Font_VirtualToRealSize(float sz)
322 //vw = ((vid.width > 0) ? vid.width : vid_width.value);
323 vh = ((vid.height > 0) ? vid.height : vid_height.value);
324 // now try to scale to our actual size:
325 sn = sz * vh / vid_conheight.value;
327 if ( sn - (float)si >= 0.5 )
332 float Font_SnapTo(float val, float snapwidth)
334 return floor(val / snapwidth + 0.5f) * snapwidth;
337 static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font);
338 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only);
339 qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt)
342 ft2_font_t *ft2, *fbfont, *fb;
351 // check if a fallback font has been specified, if it has been, and the
352 // font fails to load, use the image font as main font
353 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
355 if (dpfnt->fallbacks[i][0])
359 if (!Font_LoadFile(name, dpfnt->req_face, &dpfnt->settings, ft2))
361 if (i >= MAX_FONT_FALLBACKS)
367 strlcpy(ft2->name, name, sizeof(ft2->name));
368 ft2->image_font = true;
369 ft2->has_kerning = false;
373 ft2->image_font = false;
376 // attempt to load fallback fonts:
378 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
380 if (!dpfnt->fallbacks[i][0])
382 if (! (fb = Font_Alloc()) )
384 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
388 if (!Font_LoadFile(dpfnt->fallbacks[i], dpfnt->fallback_faces[i], &dpfnt->settings, fb))
390 if(!FS_FileExists(va("%s.tga", dpfnt->fallbacks[i])))
391 if(!FS_FileExists(va("%s.png", dpfnt->fallbacks[i])))
392 if(!FS_FileExists(va("%s.jpg", dpfnt->fallbacks[i])))
393 if(!FS_FileExists(va("%s.pcx", dpfnt->fallbacks[i])))
394 Con_Printf("Failed to load font %s for fallback %i of font %s\n", dpfnt->fallbacks[i], i, name);
399 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
401 if (Font_LoadSize(fb, Font_VirtualToRealSize(dpfnt->req_sizes[s]), true))
406 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
411 // at least one size of the fallback font loaded successfully
417 if (fbfont == ft2 && ft2->image_font)
419 // no fallbacks were loaded successfully:
426 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
428 if (Font_LoadSize(ft2, Font_VirtualToRealSize(dpfnt->req_sizes[s]), false))
433 // loading failed for every requested size
434 Font_UnloadFont(ft2);
440 //Con_Printf("%i sizes loaded\n", count);
445 static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font)
448 char filename[MAX_QPATH];
452 fs_offset_t datasize;
454 memset(font, 0, sizeof(*font));
456 if (!Font_OpenLibrary())
458 if (!r_font_disable_freetype.integer)
460 Con_Printf("WARNING: can't open load font %s\n"
461 "You need the FreeType2 DLL to load font files\n",
467 font->settings = settings;
469 namelen = strlen(name);
471 // try load direct file
472 memcpy(filename, name, namelen+1);
473 data = FS_LoadFile(filename, font_mempool, false, &datasize);
477 memcpy(filename + namelen, ".ttf", 5);
478 data = FS_LoadFile(filename, font_mempool, false, &datasize);
483 memcpy(filename + namelen, ".otf", 5);
484 data = FS_LoadFile(filename, font_mempool, false, &datasize);
489 ft2_attachment_t afm;
491 memcpy(filename + namelen, ".pfb", 5);
492 data = FS_LoadFile(filename, font_mempool, false, &datasize);
496 memcpy(filename + namelen, ".afm", 5);
497 afm.data = FS_LoadFile(filename, font_mempool, false, &afm.size);
500 Font_Attach(font, &afm);
505 // FS_LoadFile being not-quiet should print an error :)
508 Con_DPrintf("Loading font %s face %i...\n", filename, _face);
510 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
511 if (status && _face != 0)
513 Con_Printf("Failed to load face %i of %s. Falling back to face 0\n", _face, name);
515 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, 0, (FT_Face*)&font->face);
520 Con_Printf("ERROR: can't create face for %s\n"
521 "Error %i\n", // TODO: error strings
523 Font_UnloadFont(font);
527 // add the attachments
528 for (i = 0; i < font->attachmentcount; ++i)
531 memset(&args, 0, sizeof(args));
532 args.flags = FT_OPEN_MEMORY;
533 args.memory_base = (const FT_Byte*)font->attachments[i].data;
534 args.memory_size = font->attachments[i].size;
535 if (qFT_Attach_Stream((FT_Face)font->face, &args))
536 Con_Printf("Failed to add attachment %u to %s\n", (unsigned)i, font->name);
539 memcpy(font->name, name, namelen+1);
540 font->image_font = false;
541 font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
545 void Font_Postprocess_Update(ft2_font_t *fnt, int bpp, int w, int h)
548 float gausstable[2*POSTPROCESS_MAXRADIUS+1];
549 qboolean need_gauss = (!pp.buf || pp.blur != fnt->settings->blur || pp.shadowz != fnt->settings->shadowz);
550 qboolean need_circle = (!pp.buf || pp.outline != fnt->settings->outline || pp.shadowx != fnt->settings->shadowx || pp.shadowy != fnt->settings->shadowy);
551 pp.blur = fnt->settings->blur;
552 pp.outline = fnt->settings->outline;
553 pp.shadowx = fnt->settings->shadowx;
554 pp.shadowy = fnt->settings->shadowy;
555 pp.shadowz = fnt->settings->shadowz;
556 pp.outlinepadding_l = bound(0, ceil(pp.outline - pp.shadowx), POSTPROCESS_MAXRADIUS);
557 pp.outlinepadding_r = bound(0, ceil(pp.outline + pp.shadowx), POSTPROCESS_MAXRADIUS);
558 pp.outlinepadding_t = bound(0, ceil(pp.outline - pp.shadowy), POSTPROCESS_MAXRADIUS);
559 pp.outlinepadding_b = bound(0, ceil(pp.outline + pp.shadowy), POSTPROCESS_MAXRADIUS);
560 pp.blurpadding_lt = bound(0, ceil(pp.blur - pp.shadowz), POSTPROCESS_MAXRADIUS);
561 pp.blurpadding_rb = bound(0, ceil(pp.blur + pp.shadowz), POSTPROCESS_MAXRADIUS);
562 pp.padding_l = pp.blurpadding_lt + pp.outlinepadding_l;
563 pp.padding_r = pp.blurpadding_rb + pp.outlinepadding_r;
564 pp.padding_t = pp.blurpadding_lt + pp.outlinepadding_t;
565 pp.padding_b = pp.blurpadding_rb + pp.outlinepadding_b;
569 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
570 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));
571 for(x = -pp.blurpadding_rb; x <= pp.blurpadding_lt; ++x)
572 sum += gausstable[POSTPROCESS_MAXRADIUS+x];
573 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
574 pp.gausstable[POSTPROCESS_MAXRADIUS+x] = floor(gausstable[POSTPROCESS_MAXRADIUS+x] / sum * 255 + 0.5);
578 for(y = -POSTPROCESS_MAXRADIUS; y <= POSTPROCESS_MAXRADIUS; ++y)
579 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
581 float d = pp.outline + 1 - sqrt(pow(x + pp.shadowx, 2) + pow(y + pp.shadowy, 2));
582 pp.circlematrix[POSTPROCESS_MAXRADIUS+y][POSTPROCESS_MAXRADIUS+x] = (d >= 1) ? 255 : (d <= 0) ? 0 : floor(d * 255 + 0.5);
585 pp.bufwidth = w + pp.padding_l + pp.padding_r;
586 pp.bufheight = h + pp.padding_t + pp.padding_b;
587 pp.bufpitch = pp.bufwidth;
588 needed = pp.bufwidth * pp.bufheight;
589 if(!pp.buf || pp.bufsize < needed * 2)
593 pp.bufsize = needed * 4;
594 pp.buf = (unsigned char *)Mem_Alloc(font_mempool, pp.bufsize);
595 pp.buf2 = pp.buf + needed;
599 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)
602 Font_Postprocess_Update(fnt, bpp, w, h);
607 // perform operation, not exceeding the passed padding values,
608 // but possibly reducing them
609 *pad_l = min(*pad_l, pp.padding_l);
610 *pad_r = min(*pad_r, pp.padding_r);
611 *pad_t = min(*pad_t, pp.padding_t);
612 *pad_b = min(*pad_b, pp.padding_b);
614 // calculate gauss table
616 // outline the font (RGBA only)
617 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
619 // this is like mplayer subtitle rendering
620 // bbuffer, bitmap buffer: this is our font
621 // abuffer, alpha buffer: this is pp.buf
622 // tmp: this is pp.buf2
624 // create outline buffer
625 memset(pp.buf, 0, pp.bufwidth * pp.bufheight);
626 for(y = -*pad_t; y < h + *pad_b; ++y)
627 for(x = -*pad_l; x < w + *pad_r; ++x)
629 int x1 = max(-x, -pp.outlinepadding_r);
630 int y1 = max(-y, -pp.outlinepadding_b);
631 int x2 = min(pp.outlinepadding_l, w-1-x);
632 int y2 = min(pp.outlinepadding_t, h-1-y);
636 for(my = y1; my <= y2; ++my)
637 for(mx = x1; mx <= x2; ++mx)
639 cur = pp.circlematrix[POSTPROCESS_MAXRADIUS+my][POSTPROCESS_MAXRADIUS+mx] * (int)imagedata[(x+mx) * bpp + pitch * (y+my) + (bpp - 1)];
643 pp.buf[((x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t))] = (highest + 128) / 255;
646 // blur the outline buffer
647 if(pp.blur > 0 || pp.shadowz != 0)
650 for(y = 0; y < pp.bufheight; ++y)
651 for(x = 0; x < pp.bufwidth; ++x)
653 int x1 = max(-x, -pp.blurpadding_rb);
654 int x2 = min(pp.blurpadding_lt, pp.bufwidth-1-x);
657 for(mx = x1; mx <= x2; ++mx)
658 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+mx] * (int)pp.buf[(x+mx) + pp.bufpitch * y];
659 pp.buf2[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
663 for(y = 0; y < pp.bufheight; ++y)
664 for(x = 0; x < pp.bufwidth; ++x)
666 int y1 = max(-y, -pp.blurpadding_rb);
667 int y2 = min(pp.blurpadding_lt, pp.bufheight-1-y);
670 for(my = y1; my <= y2; ++my)
671 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+my] * (int)pp.buf2[x + pp.bufpitch * (y+my)];
672 pp.buf[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
676 // paste the outline below the font
677 for(y = -*pad_t; y < h + *pad_b; ++y)
678 for(x = -*pad_l; x < w + *pad_r; ++x)
680 unsigned char outlinealpha = pp.buf[(x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t)];
683 unsigned char oldalpha = imagedata[x * bpp + pitch * y + (bpp - 1)];
684 // a' = 1 - (1 - a1) (1 - a2)
685 unsigned char newalpha = 255 - ((255 - (int)outlinealpha) * (255 - (int)oldalpha)) / 255; // this is >= oldalpha
686 // c' = (a2 c2 - a1 a2 c1 + a1 c1) / a' = (a2 c2 + a1 (1 - a2) c1) / a'
687 unsigned char oldfactor = (255 * (int)oldalpha) / newalpha;
688 //unsigned char outlinefactor = ((255 - oldalpha) * (int)outlinealpha) / newalpha;
690 for(i = 0; i < bpp-1; ++i)
692 unsigned char c = imagedata[x * bpp + pitch * y + i];
693 c = (c * (int)oldfactor) / 255 /* + outlinecolor[i] * (int)outlinefactor */;
694 imagedata[x * bpp + pitch * y + i] = c;
696 imagedata[x * bpp + pitch * y + (bpp - 1)] = newalpha;
698 //imagedata[x * bpp + pitch * y + (bpp - 1)] |= 0x80;
704 // just calculate parameters
705 *pad_l = pp.padding_l;
706 *pad_r = pp.padding_r;
707 *pad_t = pp.padding_t;
708 *pad_b = pp.padding_b;
712 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size);
713 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
714 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only)
717 ft2_font_map_t *fmap, temp;
718 int gpad_l, gpad_r, gpad_t, gpad_b;
720 if (!(size > 0.001f && size < 1000.0f))
725 if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
728 for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
730 if (!font->font_maps[map_index])
732 // if a similar size has already been loaded, ignore this one
733 //abs(font->font_maps[map_index]->size - size) < 4
734 if (font->font_maps[map_index]->size == size)
738 if (map_index >= MAX_FONT_SIZES)
743 if (font->image_font)
744 fontface = (FT_Face)font->next->face;
746 fontface = (FT_Face)font->face;
747 return (Font_SearchSize(font, fontface, size) > 0);
750 Font_Postprocess(font, NULL, 0, 4, size*2, size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
752 memset(&temp, 0, sizeof(temp));
754 temp.glyphSize = CeilPowerOf2(size*2 + max(gpad_l + gpad_r, gpad_t + gpad_b));
755 temp.sfx = (1.0/64.0)/(double)size;
756 temp.sfy = (1.0/64.0)/(double)size;
757 temp.intSize = -1; // negative value: LoadMap must search now :)
758 if (!Font_LoadMap(font, &temp, 0, &fmap))
760 Con_Printf("ERROR: can't load the first character map for %s\n"
763 Font_UnloadFont(font);
766 font->font_maps[map_index] = temp.next;
768 fmap->sfx = temp.sfx;
769 fmap->sfy = temp.sfy;
771 // load the default kerning vector:
772 if (font->has_kerning)
776 for (l = 0; l < 256; ++l)
778 for (r = 0; r < 256; ++r)
781 ul = qFT_Get_Char_Index((FT_Face)font->face, l);
782 ur = qFT_Get_Char_Index((FT_Face)font->face, r);
783 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
785 fmap->kerning.kerning[l][r][0] = 0;
786 fmap->kerning.kerning[l][r][1] = 0;
790 fmap->kerning.kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
791 fmap->kerning.kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
799 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
804 int matchsize = -10000;
806 float fsize_x, fsize_y;
807 ft2_font_map_t **maps = font->font_maps;
809 fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value;
811 fsize_x = *outw * vid.width / vid_conwidth.value;
813 fsize_y = *outh * vid.height / vid_conheight.value;
818 fsize_x = fsize_y = 16;
828 for (m = 0; m < MAX_FONT_SIZES; ++m)
832 // "round up" to the bigger size if two equally-valued matches exist
833 nval = 0.5 * (abs(maps[m]->size - fsize_x) + abs(maps[m]->size - fsize_y));
834 if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
838 matchsize = maps[m]->size;
839 if (value == 0) // there is no better match
843 if (value <= r_font_size_snapping.value)
845 // do NOT keep the aspect for perfect rendering
846 if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
847 if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width;
852 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
854 if (index < 0 || index >= MAX_FONT_SIZES)
856 return font->font_maps[index];
859 static qboolean Font_SetSize(ft2_font_t *font, float w, float h)
861 if (font->currenth == h &&
862 ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
863 font->currentw == w)) // same size has been requested
867 // sorry, but freetype doesn't seem to care about other sizes
870 if (font->image_font)
872 if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
877 if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
885 qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
887 ft2_font_map_t *fmap;
888 if (!font->has_kerning || !r_font_kerning.integer)
890 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
892 fmap = font->font_maps[map_index];
895 if (left < 256 && right < 256)
897 //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
898 // quick-kerning, be aware of the size: scale it
899 if (outx) *outx = fmap->kerning.kerning[left][right][0];// * (w / (float)fmap->size);
900 if (outy) *outy = fmap->kerning.kerning[left][right][1];// * (h / (float)fmap->size);
908 //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
910 if (!Font_SetSize(font, w, h))
912 // this deserves an error message
913 Con_Printf("Failed to get kerning for %s\n", font->name);
916 ul = qFT_Get_Char_Index(font->face, left);
917 ur = qFT_Get_Char_Index(font->face, right);
918 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
920 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);
921 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);
925 if (!Font_SetSize(font, fmap->intSize, fmap->intSize))
927 // this deserves an error message
928 Con_Printf("Failed to get kerning for %s\n", font->name);
931 ul = qFT_Get_Char_Index((FT_Face)font->face, left);
932 ur = qFT_Get_Char_Index((FT_Face)font->face, right);
933 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
935 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);// * (w / (float)fmap->size);
936 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);// * (h / (float)fmap->size);
943 qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
945 return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
948 static void UnloadMapRec(ft2_font_map_t *map)
952 R_FreeTexture(map->texture);
956 UnloadMapRec(map->next);
960 void Font_UnloadFont(ft2_font_t *font)
966 Font_UnloadFont(font->next);
968 if (font->attachments && font->attachmentcount)
970 for (i = 0; i < (int)font->attachmentcount; ++i) {
971 if (font->attachments[i].data)
972 Mem_Free(font->attachments[i].data);
974 Mem_Free(font->attachments);
975 font->attachmentcount = 0;
976 font->attachments = NULL;
978 for (i = 0; i < MAX_FONT_SIZES; ++i)
980 if (font->font_maps[i])
982 UnloadMapRec(font->font_maps[i]);
983 font->font_maps[i] = NULL;
990 qFT_Done_Face((FT_Face)font->face);
995 Mem_Free(font->data);
1000 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size)
1002 float intSize = size;
1005 if (!Font_SetSize(font, intSize, intSize))
1007 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, size, intSize);
1010 if ((fontface->size->metrics.height>>6) <= size)
1014 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, size);
1021 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
1023 char map_identifier[MAX_QPATH];
1024 unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
1025 unsigned char *data;
1029 FT_Int32 load_flags;
1030 int gpad_l, gpad_r, gpad_t, gpad_b;
1033 int gR, gC; // glyph position: row and column
1035 ft2_font_map_t *map, *next;
1036 ft2_font_t *usefont;
1040 int bytesPerPixel = 4; // change the conversion loop too if you change this!
1045 if (r_font_use_alpha_textures.integer)
1048 if (font->image_font)
1049 fontface = (FT_Face)font->next->face;
1051 fontface = (FT_Face)font->face;
1053 switch(font->settings->antialias)
1056 switch(font->settings->hinting)
1059 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1063 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1067 load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1073 switch(font->settings->hinting)
1076 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1079 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
1082 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1086 load_flags = FT_LOAD_TARGET_NORMAL;
1092 //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
1094 if (font->image_font && mapstart->intSize < 0)
1095 mapstart->intSize = mapstart->size;
1096 if (mapstart->intSize < 0)
1099 mapstart->intSize = mapstart->size;
1102 if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1104 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
1107 if ((fontface->size->metrics.height>>6) <= mapstart->size)
1109 if (mapstart->intSize < 2)
1111 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
1114 --mapstart->intSize;
1117 if ((mapstart->intSize = Font_SearchSize(font, fontface, mapstart->size)) <= 0)
1119 Con_DPrintf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
1122 if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1124 Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
1128 map = (ft2_font_map_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
1131 Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
1135 Font_Postprocess(font, NULL, 0, bytesPerPixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
1137 // copy over the information
1138 map->size = mapstart->size;
1139 map->intSize = mapstart->intSize;
1140 map->glyphSize = mapstart->glyphSize;
1141 map->sfx = mapstart->sfx;
1142 map->sfy = mapstart->sfy;
1144 pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
1145 data = (unsigned char *)Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
1148 Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
1152 memset(map->width_of, 0, sizeof(map->width_of));
1154 // initialize as white texture with zero alpha
1156 while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
1158 if (bytesPerPixel == 4)
1168 map->start = mapidx * FONT_CHARS_PER_MAP;
1170 while(next->next && next->next->start < map->start)
1172 map->next = next->next;
1177 for (ch = map->start;
1178 ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
1181 FT_ULong glyphIndex;
1185 unsigned char *imagedata, *dst, *src;
1186 glyph_slot_t *mapglyph;
1188 int pad_l, pad_r, pad_t, pad_b;
1190 mapch = ch - map->start;
1192 if (developer_font.integer)
1193 Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
1196 if (gC >= FONT_CHARS_PER_LINE)
1198 gC -= FONT_CHARS_PER_LINE;
1202 imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
1203 imagedata += gpad_t * pitch + gpad_l * bytesPerPixel;
1204 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
1205 // we need the glyphIndex
1206 face = (FT_Face)font->face;
1208 if (font->image_font && mapch == ch && img_fontmap[mapch])
1210 map->glyphs[mapch].image = true;
1213 glyphIndex = qFT_Get_Char_Index(face, ch);
1214 if (glyphIndex == 0)
1216 // by convention, 0 is the "missing-glyph"-glyph
1217 // try to load from a fallback font
1218 for(usefont = font->next; usefont != NULL; usefont = usefont->next)
1220 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
1223 face = (FT_Face)usefont->face;
1224 glyphIndex = qFT_Get_Char_Index(face, ch);
1225 if (glyphIndex == 0)
1227 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1234 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1235 // now we let it use the "missing-glyph"-glyph
1236 face = (FT_Face)font->face;
1244 face = (FT_Face)font->face;
1245 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1248 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1249 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1254 glyph = face->glyph;
1255 bmp = &glyph->bitmap;
1260 if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
1261 Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
1262 if (w > map->glyphSize)
1263 w = map->glyphSize - gpad_l - gpad_r;
1264 if (h > map->glyphSize)
1268 switch (bmp->pixel_mode)
1270 case FT_PIXEL_MODE_MONO:
1271 if (developer_font.integer)
1272 Con_DPrint("glyphinfo: Pixel Mode: MONO\n");
1274 case FT_PIXEL_MODE_GRAY2:
1275 if (developer_font.integer)
1276 Con_DPrint("glyphinfo: Pixel Mode: GRAY2\n");
1278 case FT_PIXEL_MODE_GRAY4:
1279 if (developer_font.integer)
1280 Con_DPrint("glyphinfo: Pixel Mode: GRAY4\n");
1282 case FT_PIXEL_MODE_GRAY:
1283 if (developer_font.integer)
1284 Con_DPrint("glyphinfo: Pixel Mode: GRAY\n");
1287 if (developer_font.integer)
1288 Con_DPrintf("glyphinfo: Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1290 Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1293 for (y = 0; y < h; ++y)
1295 dst = imagedata + y * pitch;
1296 src = bmp->buffer + y * bmp->pitch;
1298 switch (bmp->pixel_mode)
1300 case FT_PIXEL_MODE_MONO:
1301 dst += bytesPerPixel - 1; // shift to alpha byte
1302 for (x = 0; x < bmp->width; x += 8)
1304 unsigned char ch = *src++;
1305 *dst = 255 * !!((ch & 0x80) >> 7); dst += bytesPerPixel;
1306 *dst = 255 * !!((ch & 0x40) >> 6); dst += bytesPerPixel;
1307 *dst = 255 * !!((ch & 0x20) >> 5); dst += bytesPerPixel;
1308 *dst = 255 * !!((ch & 0x10) >> 4); dst += bytesPerPixel;
1309 *dst = 255 * !!((ch & 0x08) >> 3); dst += bytesPerPixel;
1310 *dst = 255 * !!((ch & 0x04) >> 2); dst += bytesPerPixel;
1311 *dst = 255 * !!((ch & 0x02) >> 1); dst += bytesPerPixel;
1312 *dst = 255 * !!((ch & 0x01) >> 0); dst += bytesPerPixel;
1315 case FT_PIXEL_MODE_GRAY2:
1316 dst += bytesPerPixel - 1; // shift to alpha byte
1317 for (x = 0; x < bmp->width; x += 4)
1319 unsigned char ch = *src++;
1320 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1321 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1322 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1323 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1326 case FT_PIXEL_MODE_GRAY4:
1327 dst += bytesPerPixel - 1; // shift to alpha byte
1328 for (x = 0; x < bmp->width; x += 2)
1330 unsigned char ch = *src++;
1331 *dst = ( ((ch & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
1332 *dst = ( ((ch & 0x0F) ) * 0x11); dst += bytesPerPixel;
1335 case FT_PIXEL_MODE_GRAY:
1336 // in this case pitch should equal width
1337 for (tp = 0; tp < bmp->pitch; ++tp)
1338 dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
1340 //memcpy((void*)dst, (void*)src, bmp->pitch);
1341 //dst += bmp->pitch;
1352 Font_Postprocess(font, imagedata, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1354 // now fill map->glyphs[ch - map->start]
1355 mapglyph = &map->glyphs[mapch];
1359 // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1361 double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1362 //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1363 double advance = (glyph->advance.x / 64.0) / map->size;
1364 //double mWidth = (glyph->metrics.width >> 6) / map->size;
1365 //double mHeight = (glyph->metrics.height >> 6) / map->size;
1367 mapglyph->txmin = ( (double)(gC * map->glyphSize) + (double)(gpad_l - pad_l) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1368 mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1369 mapglyph->tymin = ( (double)(gR * map->glyphSize) + (double)(gpad_r - pad_r) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1370 mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1371 //mapglyph->vxmin = bearingX;
1372 //mapglyph->vxmax = bearingX + mWidth;
1373 mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
1374 mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
1375 //mapglyph->vymin = -bearingY;
1376 //mapglyph->vymax = mHeight - bearingY;
1377 mapglyph->vymin = (-glyph->bitmap_top - pad_t) / map->size;
1378 mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
1379 //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);
1380 //mapglyph->advance_x = advance * usefont->size;
1381 //mapglyph->advance_x = advance;
1382 mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1383 mapglyph->advance_y = 0;
1385 if (developer_font.integer)
1387 Con_DPrintf("glyphinfo: Glyph: %lu at (%i, %i)\n", (unsigned long)ch, gC, gR);
1388 Con_DPrintf("glyphinfo: %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1389 if (ch >= 32 && ch <= 128)
1390 Con_DPrintf("glyphinfo: Character: %c\n", (int)ch);
1391 Con_DPrintf("glyphinfo: Vertex info:\n");
1392 Con_DPrintf("glyphinfo: X: ( %f -- %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1393 Con_DPrintf("glyphinfo: Y: ( %f -- %f )\n", mapglyph->vymin, mapglyph->vymax);
1394 Con_DPrintf("glyphinfo: Texture info:\n");
1395 Con_DPrintf("glyphinfo: S: ( %f -- %f )\n", mapglyph->txmin, mapglyph->txmax);
1396 Con_DPrintf("glyphinfo: T: ( %f -- %f )\n", mapglyph->tymin, mapglyph->tymax);
1397 Con_DPrintf("glyphinfo: Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1400 map->glyphs[mapch].image = false;
1403 // create a texture from the data now
1405 if (developer_font.integer > 100)
1407 // LordHavoc: why are we writing this? And why not write it as TGA using the appropriate function?
1408 // view using `display -depth 8 -size 512x512 name_page.rgba` (be sure to use a correct -size parameter)
1409 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u.rgba", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1410 FS_WriteFile(map_identifier, data, pitch * FONT_CHAR_LINES * map->glyphSize);
1412 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1414 // probably use bytesPerPixel here instead?
1415 if (r_font_use_alpha_textures.integer)
1417 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1418 map->glyphSize * FONT_CHARS_PER_LINE,
1419 map->glyphSize * FONT_CHAR_LINES,
1420 data, TEXTYPE_ALPHA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, -1, NULL);
1422 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1423 map->glyphSize * FONT_CHARS_PER_LINE,
1424 map->glyphSize * FONT_CHAR_LINES,
1425 data, TEXTYPE_RGBA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, -1, NULL);
1431 // if the first try isn't successful, keep it with a broken texture
1432 // otherwise we retry to load it every single frame where ft2 rendering is used
1433 // this would be bad...
1434 // only `data' must be freed
1435 Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
1436 font->name, mapstart->size, mapidx);
1444 qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
1446 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1448 // the first map must have been loaded already
1449 if (!font->font_maps[map_index])
1451 return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
1454 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1456 while (start && start->start + FONT_CHARS_PER_MAP <= ch)
1457 start = start->next;
1458 if (start && start->start > ch)