上一篇文章说了freeType移植并编译通过,但是还没有调试并显示字体,今天就讲讲freeType接口的调用。
在调试过程中发现,freeType在解码时对RAM的大小是有要求的,否则会堆栈溢出,这里我们将MCIMX6Y2xxx05_ram.icf文件中的堆空间设置为0x60000,正好是384K,此条件下,调用freeType接口是没有问题的。
创建一个字体的结构体,方便gui进行调用,因为是基于C语言的,为了方便gui后期能够打包为lib库,这里使用回调函数的方式来调用字体渲染函数。
typedef struct { U16 Flags; //取值参考 GUI_TTF_MAP_FILE_MEMERY_MODE GUI_DISPCHAR * pfDispChar; //绘制字形的函数 GUI_GETCHARDISTX * pfGetCharDistX; //获取字形所占的像素个数 GUI_GETFONTINFO * pfGetFontInfo; //获取字号大小,及字体的显示方式 union{ const GUI_TTF_DATA *pTtfData; const GUI_TTF_FONT_MAP *pMap; }p; U8 height; U8 bold;//加粗,0-不加粗 U8 itailc; //斜体 void *matrix; }GUI_FONT;
上篇文章提到,因为要支持三种模式来显示字体,所以需要3个字体的结构体
typedef struct { // FONT_TYPE font_type; U8* ASCII_addr; U8* HZ_addr; U8 width; U8 height; }GUI_TTF_FONT_MAP; //ttf矢量字库文件 typedef struct{ const void * pData;//ttf字体文件地址 U32 NumBytes;//大小 }GUI_TTF_DATA; typedef struct{ GUI_TTF_DATA *pTTF; int height;//字体像素高度 int faceIndex;//有些字体文件可能包含多种字体风格。对于多种风格,此索引指定使用基于零的风格索引来创建字体。通常为 0。 }GUI_TTF_CS;
第一种,预先在PC端,用软件转换ttf对应字号的字模数据文件,然后在单片机中使用,初始化函数如下:
int GUI_MAP_CreateFont(GUI_FONT * pFont,GUI_TTF_FONT_MAP *pMap) { pFont->Flags = GUI_TTF_MAP_FILE_MEMERY_MODE; pFont->pfDispChar = (GUI_DISPCHAR*)GUI_X_DispCharMap; pFont->pfGetCharDistX = (GUI_GETCHARDISTX*)GUI_GetCharDistX; pFont->height = pMap->height; pFont->p.pMap = pMap; return 0; }
显示字体函数:
//先用工具生成字体位图文件,根据字符编码定位字库数据 /* style 获取背景色颜色 */ I16 GUI_X_DispCharMap(int x,int y,const GUI_FONT* font,U16 c,U32 color,U32 bk_color,U8 type,U8 style) { U32 addrsse = 0; U8 msb,lsb; U8 *pData; U8 fontwidth; const GUI_TTF_FONT_MAP* pMap =font->p.pMap; int i,j; U32 *pIndex; if(c&0x80){//汉字 lsb = c&0xff; msb = (c>>8)&0xff; addrsse = ( ( lsb - 0xA0) * 94 + msb - 0xA1 )*pMap->width*font->height; pData = (U8*)(pMap->HZ_addr+addrsse); fontwidth = pMap->width; }else{//assic pIndex = (U32*)pMap->ASCII_addr; addrsse = pIndex[c]; fontwidth = (addrsse>>24)&0xff; addrsse &= 0x00ffffff; pData = (U8*)(pMap->ASCII_addr+addrsse); } for(i=y;i<pMap->height +y;i++){ for(j = x;j <fontwidth + x;j++){ if(style) bk_color = INDEX2COLOR_FUNC(GUI_GetPixelIndex(j, i)); GUI_SET_PIXEL(type, i*GUI_Context.xSize+j, setcolor( pData[(i - y)*fontwidth + (j - x)] ,color,bk_color)); // GUI_SET_PIXEL(0, i*GUI_Context.xSize+j, setcolor( pData[(i - y)*fontwidth + (j - x)] ,color,bk_color)); } } return fontwidth; }
第二种,将ttf文件写入内存,然后调用freeType接口渲染字体。
字体初始化函数:主要负责文件的加载,以及字库的生成。
I16 GUI_loadFontData(GUI_TTF_DATA *ttfData,U8 *pData,U32 fileSize) { FT_Error error = 0; if(ttfData->pData==NULL) return 1; ttfData->pData = pData; ttfData->NumBytes = fileSize; error = FT_Init_FreeType(&pFTLib); error = FT_New_Memory_Face(pFTLib,pData,fileSize,0,&pFTFace); if(error) return 1; return 0; }
创建字库函数,根据字号不同,创建不同字号的字体:
int GUI_TTF_CreateFont(GUI_FONT * pFont, GUI_TTF_CS * pCS,GUI_TTF_DATA *ttfData,U8 fontsize) { pCS->pTTF = ttfData; pCS->height = fontsize; FT_Set_Char_Size(pFTFace, fontsize << 6 , fontsize << 6 , 72 , 72 ); pFont->pfDispChar = (GUI_DISPCHAR *)GUI_X_DispCharTTF; pFont->pfGetCharDistX = (GUI_GETCHARDISTX*)GUI_GetCharDistX; pFont->Flags = GUI_TTF_FILE_MEMERY_MODE; pFont->height = pCS->height; pFont->itailc = 0; pFont->matrix = NULL; pFont->bold = 0; return 0; }
最后一种,是基于文件流方式读取ttf文件,也是我们工程推荐采用的方式,可以节省大量内存资源。
加载文件函数
int GUI_loadFontFile(const char* path) { FT_Error error = 0; error = FT_Init_FreeType(&pFTLib); error = FT_New_Face(pFTLib,path,0,&pFTFace); if(error) return 1; return 0; }
创建字体函数
int GUI_TTF_CreateFontStream(GUI_FONT * pFont,U8 fontSize) { FT_Set_Char_Size(pFTFace, fontSize << 6 , fontSize << 6 , 72 , 72 ); pFont->Flags = GUI_TTF_FILE_STREAM_MODE; pFont->pfDispChar = (GUI_DISPCHAR *)GUI_X_DispCharTTF; pFont->pfGetCharDistX = (GUI_GETCHARDISTX*)GUI_GetCharDistX; pFont->height = fontSize; pFont->itailc = 0; pFont->matrix = NULL; pFont->bold = 0; return 0; }
除了第一种方式外,我们用到了freeType的以下接口:
FT_Init_FreeType //初始化freeType
FT_New_Memory_Face //从内存中加载ttf文件数据生成pFTFace
FT_New_Face//直接加载ttf文件生成pFTFace
FT_Set_Char_Size//设置字体大小
具体使用方式可以参考官方网站的api说明。
渲染的时候,有一个注意的地方,对于汉字,字符编码参数传进来是gb2312码,需要转换成unicode码,需要用到db2312转unicode码的映射表。文章末尾会附上这个映射表。
使用这个表的关键代码如下:
if(code&0x80){//判断是否为汉字编码 U8 msb = (code>>8)&0xff; U8 lsb = (code)&0xff; offset = (lsb-GB_MIN_ZONE)*GB_OFFSET_NUMBER+(msb-GB_MIN_OFFSET); code = gb2312_to_ucs2_table[offset]; }else{ y+=2;//字符统一在垂直方向向下偏移2个像素 }
而字符编码是ascii码是不需要转换的。
经测试,gui对着三种方式都能很好的支持,同时添加了部分功能的api
将字体加粗;
void GUI_TTF_SetBold(GUI_FONT * pFont,U8 bold)//设置粗体 { pFont->bold = bold; }
在渲染的函数中会判断pFont->bold的值,从而确定是否执行
FT_Outline_Embolden(&pFTFace->glyph->outline,font->bold<<6);
生成斜体:
FT_Outline_Transform(&pFTFace->glyph->outline,(FT_Matrix *)(font->matrix));
这里需要初始化一个FT_Matrix这样的结构体变量,FT_Matrix matrix ,并初始化
matrix.xx = 0x10000; matrix.xy = 0x8000; matrix.yx = 0; matrix.yy = 0x10000;
其中xx是xy的倍数关系,这个系数可以自己调节,到目前还是觉得2倍的关系,效果能够接受。
在移植过程中,参考了不少csdn上各位大神的文章,在此表示感谢,就不在此贴博文链接了(找不着链接了o(╥﹏╥)o)。
这个是unicode.h文件下载地址