一、数据结构抽象
描述字符的方式:1.位置、大小 2.点阵
点阵可以从固定大小的点阵字体文件中获得,也可以从Freetype的矢量字体文件中获得,所以我们需要抽象出一个结构体用来描述这些字符,一个结构体描述一个文字的位图,一个结构体描述一个字库操作。
1.描述一个文字的位图:
2.描述一个字库操作:
使用点阵绘制文字时:每个文字的大小一样,前后文件互不影响 ,使用Freetype绘制文字时:大小可能不同,前面文字会影响后面文字
对于单个Freetype字符,格式如下
我们要抽象出一个结构体FontBitMap,能描述一个“字符”:位置、大小、位图,我们还要抽象出一个结构体FontOpr,能描述字体的操作,比如Freetype的操作、固定点阵字体的操作
3.font_manager.h
#ifndef _FONT_MANAGER_H #define _FONT_MANAGER_H #include <common.h> typedef struct FontBitMap { int iLeftUpX; int iLeftUpY; int iWidth; int iRows; int iCurOriginX; int iCurOriginY; int iNextOriginX; int iNextOriginY; unsigned char *pucBuffer; }FontBitMap, *PFontBitMap; 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; void RegisterFont(PFontOpr ptFontOpr); void FontsRegister(void); int SelectAndInitFont(char *aFontOprName, char *aFontFileName); int SetFontSize(int iFontSize); int GetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap); #endif
第28行:链表
第30行:注册单个字符库 ,从链表里挑出想要的字库
第32行:在g_ptFonts链表里面挑出来你想要的字体文件
第33行:将找到的字体设置字体大小
第34行:将找到的字体设置位图
为了方便后续使用区域,我们进行如下操作,将区域定义到一个结构体上Region tRegion;
typedef struct FontBitMap { Region tRegion; int iCurOriginX; int iCurOriginY; int iNextOriginX; int iNextOriginY; unsigned char *pucBuffer; }FontBitMap, *PFontBitMap;
这个 tRegion在disp manager.h中有声明
typedef struct Region { int iLeftUpX; int iLeftUpY; int iWidth; int iHeigh; }Region, *PRegion;
二、实现Freetype代码
freetype.c
#include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <linux/fb.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <math.h> #include <wchar.h> #include <sys/ioctl.h> #include <font_manager.h> #include <ft2build.h> #include FT_FREETYPE_H #include FT_GLYPH_H static FT_Face g_tFace; static int g_iDefaultFontSize = 12; static int FreeTypeFontInit(char *aFineName) { FT_Library library; int error; error = FT_Init_FreeType( &library ); /* initialize library */ if (error) { printf("FT_Init_FreeType err\n"); return -1; } error = FT_New_Face(library, aFineName, 0, &g_tFace ); /* create face object */ if (error) { printf("FT_New_Face err\n"); return -1; } FT_Set_Pixel_Sizes(g_tFace, g_iDefaultFontSize, 0); return 0; } static int FreeTypeSetFontSize(int iFontSize) { FT_Set_Pixel_Sizes(g_tFace, iFontSize, 0); return 0; } static int FreeTypeGetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap) { int error; FT_Vector pen; FT_Glyph glyph; pen.x = ptFontBitMap->iCurOriginX * 64; /* 单位: 1/64像素 */ pen.y = ptFontBitMap->iCurOriginY * 64; /* 单位: 1/64像素 */ /* 转换:transformation */ FT_Set_Transform(g_tFace, 0, &pen); /* 加载位图: load glyph image into the slot (erase previous one) */ 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; } static FontOpr g_tFreetypeOpr = { .name = "freetype", .FontInit = FreeTypeFontInit, .SetFontSize = FreeTypeSetFontSize, .GetFontBitMap = FreeTypeGetFontBitMap, }; void FreetypeRegister(void) { RegisterFont(&g_tFreetypeOpr); }
static FontOpr g_tFreetypeOpr
第86~91行:实现FontOpr这个结构体里面的函数
第87行:之后可以通过freetype这个名字找到FontOpr这个结构体
第88行:初始化函数
第89行:自定义设置字符大小
第90行:得到位图并且设置坐标值
第19行:对应字体文件
第20行:对应字体大小
static int FreeTypeFontInit(char *aFineName)
第22~44行:进行初始化
第27行:初始化freetype库
第34行:加载字体文件
第41行:设置默认的字体大小
static int FreeTypeSetFontSize(int iFontSize)
第 46~50行:自定义设置字符大小
给一个字符的编码值如何得到它的位图呢?
static int FreeTypeGetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)
第52~83行:得到位图并且设置坐标值
第55行:pen用来反推原点
第57行:记录点阵
第59、60行:根据位图原点的X和Y坐标反推原点
第63行:转换
第66行:加载位图
第73行:存放点阵
第75~80行:描述一个文字的位图
void FreetypeRegister(void)
第93~96行: 进行注册,上层font_manager.c提供
三、文字管理
我们可能要用到的字体有多种,那么怎么选择用哪个字符呢,所以我们要编写一个程序管理多种字符。
font_manager.c
#include <font_manager.h> #include <string.h> static PFontOpr g_ptFonts = NULL; static PFontOpr g_ptDefaulFontOpr = NULL; void RegisterFont(PFontOpr ptFontOpr) { ptFontOpr->ptNext = g_ptFonts; g_ptFonts = ptFontOpr; } void FontsRegister(void) { extern void FreetypeRegister(void); FreetypeRegister(); } 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); } int SetFontSize(int iFontSize) { return g_ptDefaulFontOpr->SetFontSize(iFontSize); } int GetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap) { return g_ptDefaulFontOpr->GetFontBitMap(dwCode, ptFontBitMap); }
实现这四个函数
第6行: 注册的单个字库链表头放到g_ptFonts中
第7行:将22~37行找到的字库名字保存在g_ptDefaulFontOpr中
void RegisterFont(PFontOpr ptFontOpr)
第10~14行:实现与freetype.c链接的链表
void FontsRegister(void)
第16~20行:实现多个字体的注册
第18行:调用底层代码提供一个注册函数,注册单个字库,从链表里挑出想要的字库
假设你有多个字库,你要选择某一个,要提供一个SelectAndInitFont字体文件
int SelectAndInitFont(char *aFontOprName, char *aFontFileName)
第22~37行:首先在g_ptFonts链表里面挑出来你想要的字体文件
aFontOprName表示字库操作的名字,aFontFileName表示字体文件的名字
第24行:在链表里面挑出来
第27行:表示已经找到了
第29行:没有找到,继续在链表里的下一个位置找
第32行:表示不存在
第35行:如果存在则调用初始化函数
int SetFontSize(int iFontSize)
第39~42行:将找到的字体设置字体大小
int GetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)
第44~47行:将找到的字体设置位图
四、单元测试
1.font_test.c
#include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <linux/fb.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <sys/ioctl.h> #include <stdlib.h> #include <font_manager.h> #include <disp_manager.h> #define FONTDATAMAX 4096 static const unsigned char fontdata_8x16[FONTDATAMAX] = { //此处点阵省略 ......... }; /********************************************************************** * 函数名称: lcd_put_ascii * 功能描述: 在LCD指定位置上显示一个8*16的字符 * 输入参数: x坐标,y坐标,ascii码 * 输出参数: 无 * 返 回 值: 无 ***********************************************************************/ void lcd_put_ascii(int x, int y, unsigned char c) { unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16]; int i, b; unsigned char byte; for (i = 0; i < 16; i++) { byte = dots[i]; for (b = 7; b >= 0; b--) { if (byte & (1<<b)) { /* show */ PutPixel(x+7-b, y+i, 0xffffff); /* 白 */ } else { /* hide */ PutPixel(x+7-b, y+i, 0); /* 黑 */ } } } } int main(int argc, char **argv) { PDispBuff ptBuffer; int error; FontBitMap tFontBitMap; char *str= "Hello Linux"; int i = 0; int lcd_x; int lcd_y; int font_size; if (argc != 5) { printf("Usage: %s <font_file> <lcd_x> <lcd_y> <font_size>\n", argv[0]); return -1; } lcd_x = strtol(argv[2], NULL, 0); lcd_y = strtol(argv[3], NULL, 0); font_size = strtol(argv[4], NULL, 0); DisplayInit(); SelectDefaultDisplay("fb"); InitDefaultDisplay(); ptBuffer = GetDisplayBuffer(); FontsRegister(); error = SelectAndInitFont("freetype", argv[1]); if (error) { printf("SelectAndInitFont err\n"); return -1; } SetFontSize(font_size); while (str[i]) { /* get bitmap */ tFontBitMap.iCurOriginX = lcd_x; tFontBitMap.iCurOriginY = lcd_y; error = GetFontBitMap(str[i], &tFontBitMap); if (error) { printf("SelectAndInitFont err\n"); return -1; } /* draw on buffer */ DrawFontBitMap(&tFontBitMap,0xff0000);//0xff0000为红色 /* flush to lcd/web */ FlushDisplayRegion(&tFontBitMap.tRegion, ptBuffer); lcd_x = tFontBitMap.iNextOriginX; lcd_y = tFontBitMap.iNextOriginY; i++; } return 0; }
第60行:保存字符位图
第61行:想要显示的字符串
第68~72行:打印出具体用法
字体文件
显示的位置
字体的大小
第74、75行:将字符串转化成整数传入到lcd_x和lcd_y
第77行:将字符串转化成整数传入到 font_size
第86行:获得显示的设备
第88行:FontsRegister()注册单个字符库 ,从链表里挑出想要的字库
第90行:SelectAndInitFont("freetype", argv[1])去选择freetype字库操作,传入argv[1]字体文件
第97行:SetFontSize设置字体大小
第102、103行:绘制图像的原点
第104行:GetFontBitMap得到字符位图
第112行:把位图绘制出来,绘制到默认设备中
第115行:绘制的内容刷到LCD或者web上
第118、119行:绘制完后取下一个位图的坐标
2.disp_manager.c
在disp_manager.c中实现位图绘制
void DrawFontBitMap(PFontBitMap ptFontBitMap, unsigned int dwColor)
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; //printf("x = %d, y = %d\n", x, y); 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; //image[j][i] |= bitmap->buffer[q * bitmap->width + p]; if (buffer[q * width + p]) PutPixel(i, j, dwColor); } } }
3.disp_manager.h
声明函数DrawFontBitMap
void DrawFontBitMap(PFontBitMap ptFontBitMap, unsigned int dwColor);
头文件包含#include
#ifndef _DISP_MANAGER_H #define _DISP_MANAGER_H #include <common.h> #include <font_manager.h> typedef struct DispBuff { int iXres; int iYres; int iBpp; char *buff; }DispBuff, *PDispBuff; typedef struct DispOpr { char *name; int (*DeviceInit)(void); int (*DeviceExit)(void); int (*GetBuffer)(PDispBuff ptDispBuff); int (*FlushRegion)(PRegion ptRegion, PDispBuff ptDispBuff); struct DispOpr *ptNext; }DispOpr, *PDispOpr; void RegisterDisplay(PDispOpr ptDispOpr); void DisplayInit(void); int SelectDefaultDisplay(char *name); int InitDefaultDisplay(void); int PutPixel(int x, int y, unsigned int dwColor); int FlushDisplayRegion(PRegion ptRegion, PDispBuff ptDispBuff); PDispBuff GetDisplayBuffer(void); void DrawFontBitMap(PFontBitMap ptFontBitMap, unsigned int dwColor); #endif
4. common.h
因为显示系统和文字系统都需要用到这个区域结构体,所以我们直接把它拿出来定义成一个公共的头文件
#ifndef _COMMON_H #define _COMMON_H #ifndef NULL #define NULL (void *)0 #endif typedef struct Region { int iLeftUpX; int iLeftUpY; int iWidth; int iHeigh; }Region, *PRegion; #endif
5.unittest下的Makefile
EXTRA_CFLAGS := CFLAGS_file.o := #obj-y += disp_test.o #obj-y += input_test.o obj-y += font_test.o
6.font下的Makefile
EXTRA_CFLAGS := CFLAGS_file.o := obj-y += font_manager.o obj-y += freetype.o
7.顶层的Makefile
CROSS_COMPILE ?= AS = $(CROSS_COMPILE)as LD = $(CROSS_COMPILE)ld CC = $(CROSS_COMPILE)gcc CPP = $(CC) -E AR = $(CROSS_COMPILE)ar NM = $(CROSS_COMPILE)nm STRIP = $(CROSS_COMPILE)strip OBJCOPY = $(CROSS_COMPILE)objcopy OBJDUMP = $(CROSS_COMPILE)objdump export AS LD CC CPP AR NM export STRIP OBJCOPY OBJDUMP CFLAGS := -Wall -O2 -g CFLAGS += -I $(shell pwd)/include LDFLAGS := -lts -lpthread -lfreetype export CFLAGS LDFLAGS TOPDIR := $(shell pwd) export TOPDIR TARGET := test obj-y += display/ obj-y += input/ obj-y += font/ obj-y += unittest/ all : start_recursive_build $(TARGET) @echo $(TARGET) has been built! start_recursive_build: make -C ./ -f $(TOPDIR)/Makefile.build $(TARGET) : built-in.o $(CC) -o $(TARGET) built-in.o $(LDFLAGS) clean: rm -f $(shell find -name "*.o") rm -f $(TARGET) distclean: rm -f $(shell find -name "*.o") rm -f $(shell find -name "*.d") rm -f $(TARGET)
第20行:链接库
LDFLAGS := -lts -lpthread -lfreetype
第30~33行:打开这四个文件夹进行make
obj-y += display/ obj-y += input/ obj-y += font/ obj-y += unittest/
五、上板测试
book@100ask:17_font_unittest_ok$ make book@100ask:17_font_unittest_ok$ cp -r 17_font_unittest_ok/ ~/nfs_rootfs/
[root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt [root@100ask:~]# cd /mnt/ [root@100ask:/mnt/17_font_unittest_ok]# ./test ./simsun.ttc 100 400 100
注意:编译时候要将字库文件一起上传上去
运行效果: