@TOC
前言
最近看了 电子量产工具 这个项目,本专栏是对该项目的一个总结。
一、文字系统分析
文字管理器下面有 两种 字体表示方式: 点阵字符,freetype 字符,具有不同的特点和用途。
使用点阵字库显示英文字母、汉字时,大小固定,如果放大缩小则会模糊可能有锯齿出现,为了解决这个问题,引用矢量字体。
FreeType 字符使用矢量图形描述字体的轮廓和形状,可以按需缩放和变换以适应不同的显示分辨率和大小。FreeType 字符具有良好的平滑度和可扩展性,可以提供更高品质的字符显示效果。
这里主要 介绍使用 FreeType 来显示字符, 在写程序前 要先了解 FreeType 的一些基础知识。
二、使用freetype得到位图
① 初始化freetype库.
error = FT_Init_FreeType( &library );
② 加载字体文件,保存在&face 中。
FT_GlyphSlot slot;
error = FT_New_Face( library, argv[1], 0, &face );
slot = face->glyph;
③ 设置字体大小.
FT_Set_Pixel_Sizes(face, font_size, 0);
④ 根据编码值得到位图.
error = FT_Load_Char( face, chinese_str[0], FT_LOAD_RENDER );
三、结构体字体系统模块化
- 结构体模块化字体显示方式。
typedef struct FontOpr {
char *name;
int (*FontInit)(char *aFineName); //字符设备初始化
int (*SetFontSize)(int iFontSize); //设置字符大小
int (*GetFontBitMap)(unsigned int dwCode, PFontBitMap ptFontBitMap); //获取位图
struct FontOpr *ptNext;
}FontOpr, *PFontOpr;
- 结构体模块化位图。(包括基点坐标,下一个基点坐标等)
四、底层 freetype
- 实现 FontOpr 结构体。
static FontOpr g_tFreetypeOpr = {
.name = "freetype",
.FontInit = FreeTypeFontInit,
.SetFontSize = FreeTypeSetFontSize,
.GetFontBitMap = FreeTypeGetFontBitMap,
};
- 初始化 freetype 字体显示。
static int FreeTypeFontInit(char *aFineName) // aFineName 是字库名
{
FT_Library library;
int error;
error = FT_Init_FreeType( &library ); // 初始化
if (error)
{
printf("FT_Init_FreeType err\n");
return -1;
}
error = FT_New_Face(library, aFineName, 0, &g_tFace ); // 加载(打开)字体
if (error)
{
printf("FT_New_Face err\n");
return -1;
}
FT_Set_Pixel_Sizes(g_tFace, g_iDefaultFontSize, 0); // 设置字体大小
return 0;
}
- 设置字体大小就很简单了。
- 获取位图。
/* 根据 dwCode 编码值,获取位图 ptFontBitMap */
static int FreeTypeGetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)
{
int error;
FT_Vector pen;
FT_GlyphSlot slot = g_tFace->glyph;
pen.x = ptFontBitMap->iCurOriginX * 64; /* 单位: 1/64像素 */
pen.y = ptFontBitMap->iCurOriginY * 64; /* 单位: 1/64像素 */
/* 如果不涉及旋转,不涉及多个文字一起显示,就不用调用FT_Set_Transform */
FT_Set_Transform(g_tFace, 0, &pen);
/* 加载位图,根据编码值获取位图,保存在 g_tFace 中 */
error = FT_Load_Char(g_tFace, dwCode, FT_LOAD_RENDER);
if (error)
{
printf("FT_Load_Char error\n");
return -1;
}
ptFontBitMap->pucBuffer = slot->bitmap.buffer;
ptFontBitMap->tRegion.iLeftUpX = slot->bitmap_left;
ptFontBitMap->tRegion.iLeftUpY = ptFontBitMap->iCurOriginY*2 - slot->bitmap_top;
ptFontBitMap->tRegion.iWidth = slot->bitmap.width;
ptFontBitMap->tRegion.iHeigh = slot->bitmap.rows;
ptFontBitMap->iNextOriginX = ptFontBitMap->iCurOriginX + slot->advance.x / 64;
ptFontBitMap->iNextOriginY = ptFontBitMap->iCurOriginY;
return 0;
}
五、字体管理层
- 将设备注册入 链表。
- 根据 name 找到目标设备。
int SelectAndInitFont(char *aFontOprName, char *aFontFileName)
{
PFontOpr ptTmp = g_ptFonts; // 临时指针 指向 头指针
while (ptTmp)
{
if (strcmp(ptTmp->name, aFontOprName) == 0)
break;
ptTmp = ptTmp->ptNext; // 遍历链表
}
if (!ptTmp)
return -1;
g_ptDefaulFontOpr = ptTmp;
return ptTmp->FontInit(aFontFileName); // 根据字库 初始化 framebuffer
}
- 向上层提供字体大小 , 获取位图。
dwCode
是编码值,将位图保存在 函数参数 ptFontBitMap 中。 - 要在 屏幕上显示出这些位图, 还需要写 绘制位图 函数。
void DrawFontBitMap(PFontBitMap ptFontBitMap, unsigned int dwColor)
{
int i, j, p, q;
int x = ptFontBitMap->tRegion.iLeftUpX;
int y = ptFontBitMap->tRegion.iLeftUpY;
int x_max = x + ptFontBitMap->tRegion.iWidth;
int y_max = y + ptFontBitMap->tRegion.iHeigh;
int width = ptFontBitMap->tRegion.iWidth;
unsigned char *buffer = ptFontBitMap->pucBuffer;
for ( j = y, q = 0; j < y_max; j++, q++ )
{
for ( i = x, p = 0; i < x_max; i++, p++ )
{
if ( i < 0 || j < 0 ||
i >= g_tDispBuff.iXres || j >= g_tDispBuff.iYres )
continue; // 跳出本次循环
if (buffer[q * width + p])
PutPixel(i, j, dwColor); // 描点函数
}
}
}
六、测试程序
- 在 main.c 函数中 ,因为字体 在 LCD 上显示,我们要 初始化显示系统并将其 注册入链表,找到需要的显示设备。
- 通过字符编码值,找到位图,绘制位图,再改变坐标值。
测试效果:
要显示 中文时,不要忘记使用 wchar_t。
注意
: 如果想在代码中能直接使用 UNICODE 值,需要使用 wchar_t,宽字符。
#include <wchar.h>
wchar_t *str= L"你好hi";
总结
找 位图以及位图的坐标 是一个难点,要好好体会。使用 freetype 库,在编译时要进行库链接。