写在前面:
freeType2.9.1移植到A7(1)
上一篇文章说了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文件下载地址