前言:
前面我们已经把显示系统,输入系统的框架搭建好了,那么有了输入和显示,显示的内容应该是什么呢?这节就要让我们一起对显示的内容,文字系统进行搭建。
目录
一、数据结构抽象
1.描述一个文字的位图:
2.描述一个字库操作:
3.font_manager.h
二、实现Freetype代码
freetype.c
三、文字管理
font_manager.c
四、单元测试
1.font_test.c
2.disp_manager.c
3.disp_manager.h
4. common.h
5.unittest下的Makefile
6.font下的Makefile
7.顶层的Makefile
五、上板测试
一、数据结构抽象
描述字符的方式: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_Hstatic 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 4096static 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行:打印出具体用法
<font_file> 字体文件
<lcd_x> <lcd_y> 显示的位置
<font_size> 字体的大小
第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 <font_manager.h>
#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
#endiftypedef 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)nmSTRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdumpexport AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMPCFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/includeLDFLAGS := -lts -lpthread -lfreetypeexport CFLAGS LDFLAGSTOPDIR := $(shell pwd)
export TOPDIRTARGET := testobj-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
注意:编译时候要将字库文件一起上传上去
运行效果: