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 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
395 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
397 if (Font_LoadSize(fb, Font_VirtualToRealSize(dpfnt->req_sizes[s]), true))
402 Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
407 // at least one size of the fallback font loaded successfully
413 if (fbfont == ft2 && ft2->image_font)
415 // no fallbacks were loaded successfully:
422 for (s = 0; s < MAX_FONT_SIZES && dpfnt->req_sizes[s] >= 0; ++s)
424 if (Font_LoadSize(ft2, Font_VirtualToRealSize(dpfnt->req_sizes[s]), false))
429 // loading failed for every requested size
430 Font_UnloadFont(ft2);
436 //Con_Printf("%i sizes loaded\n", count);
441 static qboolean Font_LoadFile(const char *name, int _face, ft2_settings_t *settings, ft2_font_t *font)
444 char filename[MAX_QPATH];
448 fs_offset_t datasize;
450 memset(font, 0, sizeof(*font));
452 if (!Font_OpenLibrary())
454 if (!r_font_disable_freetype.integer)
456 Con_Printf("WARNING: can't open load font %s\n"
457 "You need the FreeType2 DLL to load font files\n",
463 font->settings = settings;
465 namelen = strlen(name);
467 // try load direct file
468 memcpy(filename, name, namelen+1);
469 data = FS_LoadFile(filename, font_mempool, false, &datasize);
473 memcpy(filename + namelen, ".ttf", 5);
474 data = FS_LoadFile(filename, font_mempool, false, &datasize);
479 memcpy(filename + namelen, ".otf", 5);
480 data = FS_LoadFile(filename, font_mempool, false, &datasize);
485 ft2_attachment_t afm;
487 memcpy(filename + namelen, ".pfb", 5);
488 data = FS_LoadFile(filename, font_mempool, false, &datasize);
492 memcpy(filename + namelen, ".afm", 5);
493 afm.data = FS_LoadFile(filename, font_mempool, false, &afm.size);
496 Font_Attach(font, &afm);
501 // FS_LoadFile being not-quiet should print an error :)
504 Con_DPrintf("Loading font %s face %i...\n", filename, _face);
506 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
507 if (status && _face != 0)
509 Con_Printf("Failed to load face %i of %s. Falling back to face 0\n", _face, name);
511 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, 0, (FT_Face*)&font->face);
516 Con_Printf("ERROR: can't create face for %s\n"
517 "Error %i\n", // TODO: error strings
519 Font_UnloadFont(font);
523 // add the attachments
524 for (i = 0; i < font->attachmentcount; ++i)
527 memset(&args, 0, sizeof(args));
528 args.flags = FT_OPEN_MEMORY;
529 args.memory_base = (const FT_Byte*)font->attachments[i].data;
530 args.memory_size = font->attachments[i].size;
531 if (qFT_Attach_Stream((FT_Face)font->face, &args))
532 Con_Printf("Failed to add attachment %u to %s\n", (unsigned)i, font->name);
535 memcpy(font->name, name, namelen+1);
536 font->image_font = false;
537 font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
541 void Font_Postprocess_Update(ft2_font_t *fnt, int bpp, int w, int h)
544 float gausstable[2*POSTPROCESS_MAXRADIUS+1];
545 qboolean need_gauss = (!pp.buf || pp.blur != fnt->settings->blur || pp.shadowz != fnt->settings->shadowz);
546 qboolean need_circle = (!pp.buf || pp.outline != fnt->settings->outline || pp.shadowx != fnt->settings->shadowx || pp.shadowy != fnt->settings->shadowy);
547 pp.blur = fnt->settings->blur;
548 pp.outline = fnt->settings->outline;
549 pp.shadowx = fnt->settings->shadowx;
550 pp.shadowy = fnt->settings->shadowy;
551 pp.shadowz = fnt->settings->shadowz;
552 pp.outlinepadding_l = bound(0, ceil(pp.outline - pp.shadowx), POSTPROCESS_MAXRADIUS);
553 pp.outlinepadding_r = bound(0, ceil(pp.outline + pp.shadowx), POSTPROCESS_MAXRADIUS);
554 pp.outlinepadding_t = bound(0, ceil(pp.outline - pp.shadowy), POSTPROCESS_MAXRADIUS);
555 pp.outlinepadding_b = bound(0, ceil(pp.outline + pp.shadowy), POSTPROCESS_MAXRADIUS);
556 pp.blurpadding_lt = bound(0, ceil(pp.blur - pp.shadowz), POSTPROCESS_MAXRADIUS);
557 pp.blurpadding_rb = bound(0, ceil(pp.blur + pp.shadowz), POSTPROCESS_MAXRADIUS);
558 pp.padding_l = pp.blurpadding_lt + pp.outlinepadding_l;
559 pp.padding_r = pp.blurpadding_rb + pp.outlinepadding_r;
560 pp.padding_t = pp.blurpadding_lt + pp.outlinepadding_t;
561 pp.padding_b = pp.blurpadding_rb + pp.outlinepadding_b;
565 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
566 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));
567 for(x = -pp.blurpadding_rb; x <= pp.blurpadding_lt; ++x)
568 sum += gausstable[POSTPROCESS_MAXRADIUS+x];
569 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
570 pp.gausstable[POSTPROCESS_MAXRADIUS+x] = floor(gausstable[POSTPROCESS_MAXRADIUS+x] / sum * 255 + 0.5);
574 for(y = -POSTPROCESS_MAXRADIUS; y <= POSTPROCESS_MAXRADIUS; ++y)
575 for(x = -POSTPROCESS_MAXRADIUS; x <= POSTPROCESS_MAXRADIUS; ++x)
577 float d = pp.outline + 1 - sqrt(pow(x + pp.shadowx, 2) + pow(y + pp.shadowy, 2));
578 pp.circlematrix[POSTPROCESS_MAXRADIUS+y][POSTPROCESS_MAXRADIUS+x] = (d >= 1) ? 255 : (d <= 0) ? 0 : floor(d * 255 + 0.5);
581 pp.bufwidth = w + pp.padding_l + pp.padding_r;
582 pp.bufheight = h + pp.padding_t + pp.padding_b;
583 pp.bufpitch = pp.bufwidth;
584 needed = pp.bufwidth * pp.bufheight;
585 if(!pp.buf || pp.bufsize < needed * 2)
589 pp.bufsize = needed * 4;
590 pp.buf = (unsigned char *)Mem_Alloc(font_mempool, pp.bufsize);
591 pp.buf2 = pp.buf + needed;
595 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)
598 Font_Postprocess_Update(fnt, bpp, w, h);
603 // perform operation, not exceeding the passed padding values,
604 // but possibly reducing them
605 *pad_l = min(*pad_l, pp.padding_l);
606 *pad_r = min(*pad_r, pp.padding_r);
607 *pad_t = min(*pad_t, pp.padding_t);
608 *pad_b = min(*pad_b, pp.padding_b);
610 // calculate gauss table
612 // outline the font (RGBA only)
613 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
615 // this is like mplayer subtitle rendering
616 // bbuffer, bitmap buffer: this is our font
617 // abuffer, alpha buffer: this is pp.buf
618 // tmp: this is pp.buf2
620 // create outline buffer
621 memset(pp.buf, 0, pp.bufwidth * pp.bufheight);
622 for(y = -*pad_t; y < h + *pad_b; ++y)
623 for(x = -*pad_l; x < w + *pad_r; ++x)
625 int x1 = max(-x, -pp.outlinepadding_r);
626 int y1 = max(-y, -pp.outlinepadding_b);
627 int x2 = min(pp.outlinepadding_l, w-1-x);
628 int y2 = min(pp.outlinepadding_t, h-1-y);
632 for(my = y1; my <= y2; ++my)
633 for(mx = x1; mx <= x2; ++mx)
635 cur = pp.circlematrix[POSTPROCESS_MAXRADIUS+my][POSTPROCESS_MAXRADIUS+mx] * (int)imagedata[(x+mx) * bpp + pitch * (y+my) + (bpp - 1)];
639 pp.buf[((x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t))] = (highest + 128) / 255;
642 // blur the outline buffer
643 if(pp.blur > 0 || pp.shadowz != 0)
646 for(y = 0; y < pp.bufheight; ++y)
647 for(x = 0; x < pp.bufwidth; ++x)
649 int x1 = max(-x, -pp.blurpadding_rb);
650 int x2 = min(pp.blurpadding_lt, pp.bufwidth-1-x);
653 for(mx = x1; mx <= x2; ++mx)
654 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+mx] * (int)pp.buf[(x+mx) + pp.bufpitch * y];
655 pp.buf2[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
659 for(y = 0; y < pp.bufheight; ++y)
660 for(x = 0; x < pp.bufwidth; ++x)
662 int y1 = max(-y, -pp.blurpadding_rb);
663 int y2 = min(pp.blurpadding_lt, pp.bufheight-1-y);
666 for(my = y1; my <= y2; ++my)
667 blurred += pp.gausstable[POSTPROCESS_MAXRADIUS+my] * (int)pp.buf2[x + pp.bufpitch * (y+my)];
668 pp.buf[x + pp.bufpitch * y] = bound(0, blurred, 65025) / 255;
672 // paste the outline below the font
673 for(y = -*pad_t; y < h + *pad_b; ++y)
674 for(x = -*pad_l; x < w + *pad_r; ++x)
676 unsigned char outlinealpha = pp.buf[(x + pp.padding_l) + pp.bufpitch * (y + pp.padding_t)];
679 unsigned char oldalpha = imagedata[x * bpp + pitch * y + (bpp - 1)];
680 // a' = 1 - (1 - a1) (1 - a2)
681 unsigned char newalpha = 255 - ((255 - (int)outlinealpha) * (255 - (int)oldalpha)) / 255; // this is >= oldalpha
682 // c' = (a2 c2 - a1 a2 c1 + a1 c1) / a' = (a2 c2 + a1 (1 - a2) c1) / a'
683 unsigned char oldfactor = (255 * (int)oldalpha) / newalpha;
684 //unsigned char outlinefactor = ((255 - oldalpha) * (int)outlinealpha) / newalpha;
686 for(i = 0; i < bpp-1; ++i)
688 unsigned char c = imagedata[x * bpp + pitch * y + i];
689 c = (c * (int)oldfactor) / 255 /* + outlinecolor[i] * (int)outlinefactor */;
690 imagedata[x * bpp + pitch * y + i] = c;
692 imagedata[x * bpp + pitch * y + (bpp - 1)] = newalpha;
694 //imagedata[x * bpp + pitch * y + (bpp - 1)] |= 0x80;
700 // just calculate parameters
701 *pad_l = pp.padding_l;
702 *pad_r = pp.padding_r;
703 *pad_t = pp.padding_t;
704 *pad_b = pp.padding_b;
708 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size);
709 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
710 static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean check_only)
713 ft2_font_map_t *fmap, temp;
714 int gpad_l, gpad_r, gpad_t, gpad_b;
716 if (!(size > 0.001f && size < 1000.0f))
721 if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
724 for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
726 if (!font->font_maps[map_index])
728 // if a similar size has already been loaded, ignore this one
729 //abs(font->font_maps[map_index]->size - size) < 4
730 if (font->font_maps[map_index]->size == size)
734 if (map_index >= MAX_FONT_SIZES)
739 if (font->image_font)
740 fontface = (FT_Face)font->next->face;
742 fontface = (FT_Face)font->face;
743 return (Font_SearchSize(font, fontface, size) > 0);
746 Font_Postprocess(font, NULL, 0, 4, size*2, size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
748 memset(&temp, 0, sizeof(temp));
750 temp.glyphSize = CeilPowerOf2(size*2 + max(gpad_l + gpad_r, gpad_t + gpad_b));
751 temp.sfx = (1.0/64.0)/(double)size;
752 temp.sfy = (1.0/64.0)/(double)size;
753 temp.intSize = -1; // negative value: LoadMap must search now :)
754 if (!Font_LoadMap(font, &temp, 0, &fmap))
756 Con_Printf("ERROR: can't load the first character map for %s\n"
759 Font_UnloadFont(font);
762 font->font_maps[map_index] = temp.next;
764 fmap->sfx = temp.sfx;
765 fmap->sfy = temp.sfy;
767 // load the default kerning vector:
768 if (font->has_kerning)
772 for (l = 0; l < 256; ++l)
774 for (r = 0; r < 256; ++r)
777 ul = qFT_Get_Char_Index((FT_Face)font->face, l);
778 ur = qFT_Get_Char_Index((FT_Face)font->face, r);
779 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
781 fmap->kerning.kerning[l][r][0] = 0;
782 fmap->kerning.kerning[l][r][1] = 0;
786 fmap->kerning.kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
787 fmap->kerning.kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
795 int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
800 int matchsize = -10000;
802 float fsize_x, fsize_y;
803 ft2_font_map_t **maps = font->font_maps;
805 fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value;
807 fsize_x = *outw * vid.width / vid_conwidth.value;
809 fsize_y = *outh * vid.height / vid_conheight.value;
814 fsize_x = fsize_y = 16;
824 for (m = 0; m < MAX_FONT_SIZES; ++m)
828 // "round up" to the bigger size if two equally-valued matches exist
829 nval = 0.5 * (abs(maps[m]->size - fsize_x) + abs(maps[m]->size - fsize_y));
830 if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
834 matchsize = maps[m]->size;
835 if (value == 0) // there is no better match
839 if (value <= r_font_size_snapping.value)
841 // do NOT keep the aspect for perfect rendering
842 if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
843 if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width;
848 ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
850 if (index < 0 || index >= MAX_FONT_SIZES)
852 return font->font_maps[index];
855 static qboolean Font_SetSize(ft2_font_t *font, float w, float h)
857 if (font->currenth == h &&
858 ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
859 font->currentw == w)) // same size has been requested
863 // sorry, but freetype doesn't seem to care about other sizes
866 if (font->image_font)
868 if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
873 if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
881 qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
883 ft2_font_map_t *fmap;
884 if (!font->has_kerning || !r_font_kerning.integer)
886 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
888 fmap = font->font_maps[map_index];
891 if (left < 256 && right < 256)
893 //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
894 // quick-kerning, be aware of the size: scale it
895 if (outx) *outx = fmap->kerning.kerning[left][right][0];// * (w / (float)fmap->size);
896 if (outy) *outy = fmap->kerning.kerning[left][right][1];// * (h / (float)fmap->size);
904 //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
906 if (!Font_SetSize(font, w, h))
908 // this deserves an error message
909 Con_Printf("Failed to get kerning for %s\n", font->name);
912 ul = qFT_Get_Char_Index(font->face, left);
913 ur = qFT_Get_Char_Index(font->face, right);
914 if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
916 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);
917 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);
921 if (!Font_SetSize(font, fmap->intSize, fmap->intSize))
923 // this deserves an error message
924 Con_Printf("Failed to get kerning for %s\n", font->name);
927 ul = qFT_Get_Char_Index((FT_Face)font->face, left);
928 ur = qFT_Get_Char_Index((FT_Face)font->face, right);
929 if (qFT_Get_Kerning((FT_Face)font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
931 if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);// * (w / (float)fmap->size);
932 if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);// * (h / (float)fmap->size);
939 qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
941 return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
944 static void UnloadMapRec(ft2_font_map_t *map)
948 R_FreeTexture(map->texture);
952 UnloadMapRec(map->next);
956 void Font_UnloadFont(ft2_font_t *font)
962 Font_UnloadFont(font->next);
964 if (font->attachments && font->attachmentcount)
966 for (i = 0; i < font->attachmentcount; ++i) {
967 if (font->attachments[i].data)
968 Mem_Free(font->attachments[i].data);
970 Mem_Free(font->attachments);
971 font->attachmentcount = 0;
972 font->attachments = NULL;
974 for (i = 0; i < MAX_FONT_SIZES; ++i)
976 if (font->font_maps[i])
978 UnloadMapRec(font->font_maps[i]);
979 font->font_maps[i] = NULL;
986 qFT_Done_Face((FT_Face)font->face);
991 Mem_Free(font->data);
996 static float Font_SearchSize(ft2_font_t *font, FT_Face fontface, float size)
998 float intSize = size;
1001 if (!Font_SetSize(font, intSize, intSize))
1003 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, size, intSize);
1006 if ((fontface->size->metrics.height>>6) <= size)
1010 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, size);
1017 static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
1019 char map_identifier[MAX_QPATH];
1020 unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
1021 unsigned char *data;
1025 FT_Int32 load_flags;
1026 int gpad_l, gpad_r, gpad_t, gpad_b;
1029 int gR, gC; // glyph position: row and column
1031 ft2_font_map_t *map, *next;
1032 ft2_font_t *usefont;
1036 int bytesPerPixel = 4; // change the conversion loop too if you change this!
1041 if (r_font_use_alpha_textures.integer)
1044 if (font->image_font)
1045 fontface = (FT_Face)font->next->face;
1047 fontface = (FT_Face)font->face;
1049 switch(font->settings->antialias)
1052 switch(font->settings->hinting)
1055 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1059 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1063 load_flags = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME;
1069 switch(font->settings->hinting)
1072 load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1075 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
1078 load_flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
1082 load_flags = FT_LOAD_TARGET_NORMAL;
1088 //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
1090 if (font->image_font && mapstart->intSize < 0)
1091 mapstart->intSize = mapstart->size;
1092 if (mapstart->intSize < 0)
1095 mapstart->intSize = mapstart->size;
1098 if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1100 Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
1103 if ((fontface->size->metrics.height>>6) <= mapstart->size)
1105 if (mapstart->intSize < 2)
1107 Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
1110 --mapstart->intSize;
1113 if ((mapstart->intSize = Font_SearchSize(font, fontface, mapstart->size)) <= 0)
1115 Con_DPrintf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
1118 if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
1120 Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
1124 map = (ft2_font_map_t *)Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
1127 Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
1131 Font_Postprocess(font, NULL, 0, bytesPerPixel, mapstart->size*2, mapstart->size*2, &gpad_l, &gpad_r, &gpad_t, &gpad_b);
1133 // copy over the information
1134 map->size = mapstart->size;
1135 map->intSize = mapstart->intSize;
1136 map->glyphSize = mapstart->glyphSize;
1137 map->sfx = mapstart->sfx;
1138 map->sfy = mapstart->sfy;
1140 pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
1141 data = (unsigned char *)Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
1144 Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
1148 memset(map->width_of, 0, sizeof(map->width_of));
1150 // initialize as white texture with zero alpha
1152 while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
1154 if (bytesPerPixel == 4)
1164 map->start = mapidx * FONT_CHARS_PER_MAP;
1166 while(next->next && next->next->start < map->start)
1168 map->next = next->next;
1173 for (ch = map->start;
1174 ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
1177 FT_ULong glyphIndex;
1181 unsigned char *imagedata, *dst, *src;
1182 glyph_slot_t *mapglyph;
1184 int pad_l, pad_r, pad_t, pad_b;
1186 mapch = ch - map->start;
1188 if (developer_font.integer)
1189 Con_DPrint("glyphinfo: ------------- GLYPH INFO -----------------\n");
1192 if (gC >= FONT_CHARS_PER_LINE)
1194 gC -= FONT_CHARS_PER_LINE;
1198 imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
1199 imagedata += gpad_t * pitch + gpad_l * bytesPerPixel;
1200 //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
1201 // we need the glyphIndex
1202 face = (FT_Face)font->face;
1204 if (font->image_font && mapch == ch && img_fontmap[mapch])
1206 map->glyphs[mapch].image = true;
1209 glyphIndex = qFT_Get_Char_Index(face, ch);
1210 if (glyphIndex == 0)
1212 // by convention, 0 is the "missing-glyph"-glyph
1213 // try to load from a fallback font
1214 for(usefont = font->next; usefont != NULL; usefont = usefont->next)
1216 if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
1219 face = (FT_Face)usefont->face;
1220 glyphIndex = qFT_Get_Char_Index(face, ch);
1221 if (glyphIndex == 0)
1223 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1230 //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1231 // now we let it use the "missing-glyph"-glyph
1232 face = (FT_Face)font->face;
1240 face = (FT_Face)font->face;
1241 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER | load_flags);
1244 //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
1245 Con_DPrintf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
1250 glyph = face->glyph;
1251 bmp = &glyph->bitmap;
1256 if (w > (map->glyphSize - gpad_l - gpad_r) || h > (map->glyphSize - gpad_t - gpad_b)) {
1257 Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
1258 if (w > map->glyphSize)
1259 w = map->glyphSize - gpad_l - gpad_r;
1260 if (h > map->glyphSize)
1264 switch (bmp->pixel_mode)
1266 case FT_PIXEL_MODE_MONO:
1267 if (developer_font.integer)
1268 Con_DPrint("glyphinfo: Pixel Mode: MONO\n");
1270 case FT_PIXEL_MODE_GRAY2:
1271 if (developer_font.integer)
1272 Con_DPrint("glyphinfo: Pixel Mode: GRAY2\n");
1274 case FT_PIXEL_MODE_GRAY4:
1275 if (developer_font.integer)
1276 Con_DPrint("glyphinfo: Pixel Mode: GRAY4\n");
1278 case FT_PIXEL_MODE_GRAY:
1279 if (developer_font.integer)
1280 Con_DPrint("glyphinfo: Pixel Mode: GRAY\n");
1283 if (developer_font.integer)
1284 Con_DPrintf("glyphinfo: Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
1286 Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
1289 for (y = 0; y < h; ++y)
1291 dst = imagedata + y * pitch;
1292 src = bmp->buffer + y * bmp->pitch;
1294 switch (bmp->pixel_mode)
1296 case FT_PIXEL_MODE_MONO:
1297 dst += bytesPerPixel - 1; // shift to alpha byte
1298 for (x = 0; x < bmp->width; x += 8)
1300 unsigned char ch = *src++;
1301 *dst = 255 * !!((ch & 0x80) >> 7); dst += bytesPerPixel;
1302 *dst = 255 * !!((ch & 0x40) >> 6); dst += bytesPerPixel;
1303 *dst = 255 * !!((ch & 0x20) >> 5); dst += bytesPerPixel;
1304 *dst = 255 * !!((ch & 0x10) >> 4); dst += bytesPerPixel;
1305 *dst = 255 * !!((ch & 0x08) >> 3); dst += bytesPerPixel;
1306 *dst = 255 * !!((ch & 0x04) >> 2); dst += bytesPerPixel;
1307 *dst = 255 * !!((ch & 0x02) >> 1); dst += bytesPerPixel;
1308 *dst = 255 * !!((ch & 0x01) >> 0); dst += bytesPerPixel;
1311 case FT_PIXEL_MODE_GRAY2:
1312 dst += bytesPerPixel - 1; // shift to alpha byte
1313 for (x = 0; x < bmp->width; x += 4)
1315 unsigned char ch = *src++;
1316 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1317 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1318 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1319 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
1322 case FT_PIXEL_MODE_GRAY4:
1323 dst += bytesPerPixel - 1; // shift to alpha byte
1324 for (x = 0; x < bmp->width; x += 2)
1326 unsigned char ch = *src++;
1327 *dst = ( ((ch & 0xF0) >> 4) * 0x11); dst += bytesPerPixel;
1328 *dst = ( ((ch & 0x0F) ) * 0x11); dst += bytesPerPixel;
1331 case FT_PIXEL_MODE_GRAY:
1332 // in this case pitch should equal width
1333 for (tp = 0; tp < bmp->pitch; ++tp)
1334 dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
1336 //memcpy((void*)dst, (void*)src, bmp->pitch);
1337 //dst += bmp->pitch;
1348 Font_Postprocess(font, imagedata, pitch, bytesPerPixel, w, h, &pad_l, &pad_r, &pad_t, &pad_b);
1350 // now fill map->glyphs[ch - map->start]
1351 mapglyph = &map->glyphs[mapch];
1355 // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
1357 double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
1358 //double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
1359 double advance = (glyph->advance.x / 64.0) / map->size;
1360 //double mWidth = (glyph->metrics.width >> 6) / map->size;
1361 //double mHeight = (glyph->metrics.height >> 6) / map->size;
1363 mapglyph->txmin = ( (double)(gC * map->glyphSize) + (double)(gpad_l - pad_l) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1364 mapglyph->txmax = mapglyph->txmin + (double)(bmp->width + pad_l + pad_r) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
1365 mapglyph->tymin = ( (double)(gR * map->glyphSize) + (double)(gpad_r - pad_r) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1366 mapglyph->tymax = mapglyph->tymin + (double)(bmp->rows + pad_t + pad_b) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
1367 //mapglyph->vxmin = bearingX;
1368 //mapglyph->vxmax = bearingX + mWidth;
1369 mapglyph->vxmin = (glyph->bitmap_left - pad_l) / map->size;
1370 mapglyph->vxmax = mapglyph->vxmin + (bmp->width + pad_l + pad_r) / map->size; // don't ask
1371 //mapglyph->vymin = -bearingY;
1372 //mapglyph->vymax = mHeight - bearingY;
1373 mapglyph->vymin = (-glyph->bitmap_top - pad_t) / map->size;
1374 mapglyph->vymax = mapglyph->vymin + (bmp->rows + pad_t + pad_b) / map->size;
1375 //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);
1376 //mapglyph->advance_x = advance * usefont->size;
1377 //mapglyph->advance_x = advance;
1378 mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
1379 mapglyph->advance_y = 0;
1381 if (developer_font.integer)
1383 Con_DPrintf("glyphinfo: Glyph: %lu at (%i, %i)\n", (unsigned long)ch, gC, gR);
1384 Con_DPrintf("glyphinfo: %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
1385 if (ch >= 32 && ch <= 128)
1386 Con_DPrintf("glyphinfo: Character: %c\n", (int)ch);
1387 Con_DPrintf("glyphinfo: Vertex info:\n");
1388 Con_DPrintf("glyphinfo: X: ( %f -- %f )\n", mapglyph->vxmin, mapglyph->vxmax);
1389 Con_DPrintf("glyphinfo: Y: ( %f -- %f )\n", mapglyph->vymin, mapglyph->vymax);
1390 Con_DPrintf("glyphinfo: Texture info:\n");
1391 Con_DPrintf("glyphinfo: S: ( %f -- %f )\n", mapglyph->txmin, mapglyph->txmax);
1392 Con_DPrintf("glyphinfo: T: ( %f -- %f )\n", mapglyph->tymin, mapglyph->tymax);
1393 Con_DPrintf("glyphinfo: Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
1396 map->glyphs[mapch].image = false;
1399 // create a texture from the data now
1401 if (developer_font.integer > 100)
1403 // LordHavoc: why are we writing this? And why not write it as TGA using the appropriate function?
1404 // view using `display -depth 8 -size 512x512 name_page.rgba` (be sure to use a correct -size parameter)
1405 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u.rgba", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1406 FS_WriteFile(map_identifier, data, pitch * FONT_CHAR_LINES * map->glyphSize);
1408 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
1410 // probably use bytesPerPixel here instead?
1411 if (r_font_use_alpha_textures.integer)
1413 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1414 map->glyphSize * FONT_CHARS_PER_LINE,
1415 map->glyphSize * FONT_CHAR_LINES,
1416 data, TEXTYPE_ALPHA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, -1, NULL);
1418 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
1419 map->glyphSize * FONT_CHARS_PER_LINE,
1420 map->glyphSize * FONT_CHAR_LINES,
1421 data, TEXTYPE_RGBA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, -1, NULL);
1427 // if the first try isn't successful, keep it with a broken texture
1428 // otherwise we retry to load it every single frame where ft2 rendering is used
1429 // this would be bad...
1430 // only `data' must be freed
1431 Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
1432 font->name, mapstart->size, mapidx);
1440 qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
1442 if (map_index < 0 || map_index >= MAX_FONT_SIZES)
1444 // the first map must have been loaded already
1445 if (!font->font_maps[map_index])
1447 return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
1450 ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
1452 while (start && start->start + FONT_CHARS_PER_MAP <= ch)
1453 start = start->next;
1454 if (start && start->start > ch)