网上的FreeType2例子太少,能显示汉字的比较难找,C语言代码写的更难找,能找到的,基本上是被转载了N遍的同一个示例代码,基本上解决不了我的问题。
于是乎,花费了不少时间才完成了这些代码。
主要是先解决编码问题,需要用wchar_t类型保存unicode编码的字符,字符串常量倒好弄,例如:wchar_t str[]=L"一段文本"; 编译时编译器就自动帮你转换好,但前提是源码文件的编码要为UTF-8,其它编码,例如ASCII,编译时会报错。
而字符串变量的话,需要程序自己转换,UTF-8转Unicode,代码在这里:
https://github.com/lc-soft/LCUI/blob/master/src/font/charset.c
看utf8_to_unicode函数的代码。
其次是要知道如何获取一个字的位图的相关度量,例如:字形的宽、高、左边界距、上边界距、水平跨距等等。
以前看中译版本的FreeType文档,没看明白,最近看了后,终于知道了如何获取字形的这些度量。
我目前使用的代码在这里:
https://github.com/lc-soft/LCUI/blob/master/src/font/bitmapfont.c
主要看Convert_FTGlyph函数和Get_FontBMP函数的代码。
Get_FontBMP函数只获取单个字的位图信息,要获取字符串的话,连续调用Get_FontBMP函数并保存得到的字体数据,之后根据字体数据中的信息绘制到目标面上即可。
除了看代码外,建议看FreeType2的文档,网上有中译版本。
文字绘制的效果截图:
主要演示彩色文本的渲染,不同大小字体的绘制,字体使用的是 微软雅黑。
以下代码中用到的结构体:
/******************************保存字体信息********************************/ struct _LCUI_Font/* 字体信息数据 */ { int type; /* 类型(DEFAULT / CUSTOM) */ LCUI_String font_file; /* 字体文件的路径 */ LCUI_String family_name; /* 字体名称 */ LCUI_String style_name; /* 字体风格名称 */ void* ft_lib; /* FreeType2库的句柄 */ void* ft_face; /* FreeType2的face对象的句柄 */ int load_flags; /* 字形载入标志 */ int render_mode; /* 字形转换模式标志 */ int status; /* 状态,是否打开了字体库 */ }; /************************************************************************/ /********** 保存字体位图数据 ***************/ struct _LCUI_FontBMP { int top; /* 与顶边框的距离 */ int left; /* 与左边框的距离 */ int width; /* 位图宽度 */ int rows; /* 位图行数 */ int pitch; uchar_t *buffer; /* 字体位图数据 */ short num_grays; char pixel_mode; LCUI_Pos advance; /* XY轴的跨距 */ }; /*****************************************/
以下是部分代码,Convert_FTGlyph函数中主要获取字体位图的相关度量信息。
Open_Fontfile函数是打开指定路径的字体文件,并将FT库的句柄及face对象指针保存至结构体中。
int Open_Fontfile ( LCUI_Font * font_data , char * fontfile )/* 打开指定路径中的字体文件,并保存数据至LCUI_Font结构体中 */{#ifdef USE_FREETYPEint type ;FT_Library library ;FT_Face face ;FT_Error face_error = 0 , lib_error = 0 ;type = font_data -> type ;if ( font_data -> status == ACTIVE ) {/* 如果字体文件路径无效,或该路径和默认的字体文件路径一样,则退出函数 */if ( ! fontfile || ! Strcmp ( & font_data -> font_file , fontfile ) ) {return 0 ;}else if ( Strcmp ( & font_data -> font_file ,LCUI_Sys . default_font . font_file . string )) {/* 否则,如果不一样,就将type赋值为CUSTOM,表示自定义 */type = CUSTOM ;}}else if ( ! fontfile ) {return - 1 ;}/* 初始化FreeType库 */lib_error = FT_Init_FreeType ( & library );/* 当初始化库时发生了一个错误 */if ( lib_error ) {printf ( "open fontfile: " FT_INIT_ERROR );return - 1 ;}face_error = FT_New_Face ( library , fontfile , 0 , & face );if ( face_error ) {FT_Done_FreeType ( library );if ( face_error == FT_Err_Unknown_File_Format ) {/* 未知文件格式 */printf ( "open fontfile: " FT_UNKNOWN_FILE_FORMAT );} else {/* 打开错误 */printf ( "open fontfile: " FT_OPEN_FILE_ERROR );}/* 打印错误信息 */perror ( fontfile );return - 1 ;}/* 打印字体信息 */printf ( "=============== font info ============== \n ""family name: %s \n ""style name : %s \n ""======================================== \n " ,face -> family_name ,face -> style_name );/* 先处理掉之前保存的字体信息 */Font_Free ( font_data );/* 保存新的字体信息 */Strcpy ( & font_data -> family_name , face -> family_name );Strcpy ( & font_data -> style_name , face -> style_name );Strcpy ( & font_data -> font_file , fontfile );font_data -> type = type ;font_data -> status = ACTIVE ;font_data -> ft_lib = library ;font_data -> ft_face = face ;return 0 ;#elseprintf ( "warning: not font engine support! \n " );return - 1 ;#endif}/* 如果定义了USE_FREETYPE宏定义,则使用FreeType字体引擎处理字体数据 */#ifdef USE_FREETYPEstatic intConvert_FTGlyph ( LCUI_FontBMP * des , FT_GlyphSlot slot , int render_mode )/* 转换FT_GlyphSlot类型数据为LCUI_FontBMP */{static FT_Error error ;static size_t size ;static FT_BitmapGlyph bitmap_glyph ;static FT_Glyph glyph ;/* 从字形槽中提取一个字形图像* 请注意,创建的FT_Glyph对象必须与FT_Done_Glyph成对使用 */error = FT_Get_Glyph ( slot , & glyph );if ( error ) {return - 1 ;}/*---------------------- 打印字体信息 --------------------------printf(" width= %ld, met->height= %ld\n""horiBearingX = %ld, horiBearingY = %ld, horiAdvance = %ld\n""vertBearingX = %ld, vertBearingY = %ld, vertAdvance = %ld\n",slot->metrics.width>>6, slot->metrics.height>>6,slot->metrics.horiBearingX>>6, slot->metrics.horiBearingY>>6,slot->metrics.horiAdvance>>6, slot->metrics.vertBearingX>>6,slot->metrics.vertBearingY>>6, slot->metrics.vertAdvance>>6 );------------------------------------------------------------*/if ( glyph -> format != FT_GLYPH_FORMAT_BITMAP ) {error = FT_Glyph_To_Bitmap ( & glyph , render_mode , 0 , 1 );if ( error ) {return - 1 ;}}bitmap_glyph = ( FT_BitmapGlyph ) glyph ;/** FT_Glyph_Metrics结构体中保存字形度量,通过face->glyph->metrics结* 构访问,可得到字形的宽、高、左边界距、上边界距、水平跨距等等。* 注意:因为不是所有的字体都包含垂直度量,当FT_HAS_VERTICAL为假时,* vertBearingX,vertBearingY和vertAdvance的值是不可靠的,目前暂不考虑* 此情况的处理。* */des -> top = bitmap_glyph -> top ;des -> left = slot -> metrics . horiBearingX >> 6 ;des -> rows = bitmap_glyph -> bitmap . rows ;des -> width = bitmap_glyph -> bitmap . width ;des -> pixel_mode = bitmap_glyph -> bitmap . pixel_mode ;des -> num_grays = bitmap_glyph -> bitmap . num_grays ;des -> advance . x = slot -> metrics . horiAdvance >> 6 ; /* 水平跨距 */des -> advance . y = slot -> metrics . vertAdvance >> 6 ; /* 垂直跨距 *//* 分配内存,用于保存字体位图 */size = des -> rows * des -> width * sizeof ( uchar_t );des -> buffer = malloc ( size );if ( ! des -> buffer ) {FT_Done_Glyph ( glyph );return - 1 ;}/* 拷贝至该内存空间内 */memcpy ( des -> buffer , bitmap_glyph -> bitmap . buffer , size );FT_Done_Glyph ( glyph );return size ;}#endifintGet_FontBMP ( LCUI_Font * font_data , wchar_t ch ,int pixel_size , LCUI_FontBMP * out_bitmap )/** 功能:获取单个wchar_t型字符的字体位图数据* 说明:LCUI_Font结构体中储存着已被打开的字体文件句柄和face对象的句柄,如果字体文件* 已经被成功打开一次,此函数不会再次打开字体文件。*/{#ifdef USE_FREETYPEsize_t size ;BOOL have_space = FALSE ;FT_Face p_FT_Face = NULL ; /* face对象的句柄 */FT_Error error ;if ( font_data ) {/* 如果font_data有效,则打开font_data中的指定的字体文件,并将字体文件* 和face对象的句柄保存至结构体中。* 当然,如果LCUI_Font结构体有有效的字体文件和face对象的句柄,就不会再重新* 打开字体文件。*/if ( ! font_data -> ft_face || ! font_data -> ft_lib ) {error = Open_Fontfile ( font_data ,font_data -> font_file . string );if ( error ) {Get_Default_FontBMP ( ch , out_bitmap );return 1 ;}}/* 引用face对象句柄 */p_FT_Face = font_data -> ft_face ;} else {/* 使用内置的字体位图 */Get_Default_FontBMP ( ch , out_bitmap );return - 1 ;}/* 设定为UNICODE,默认的也是 */FT_Select_Charmap ( p_FT_Face , FT_ENCODING_UNICODE );/* 设定字体尺寸 */FT_Set_Pixel_Sizes ( p_FT_Face , 0 , pixel_size );/* 如果是空格 */if ( ch == ' ' ) {ch = 'a' ;have_space = TRUE ;}/* 这个函数只是简单地调用FT_Get_Char_Index和FT_Load_Glyph */error = FT_Load_Char ( p_FT_Face , ch , font_data -> load_flags );if ( error ) {return error ;}size = Convert_FTGlyph ( out_bitmap , p_FT_Face -> glyph , font_data -> render_mode );/* 如果是空格则将位图内容清空 */if ( have_space ) {memset ( out_bitmap -> buffer , 0 , size );}return 0 ;#elseGet_Default_FontBMP ( ch , out_bitmap );return - 1 ;#endif}