前言
最近看了 电子量产工具 这个项目,本专栏是对该项目的一个总结。
一、文字系统分析
文字管理器下面有 两种 字体表示方式: 点阵字符,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 库,在编译时要进行库链接。