前言:
前面我们已经把显示系统,输入系统的框架搭建好了,那么有了输入和显示,显示的内容应该是什么呢?这节就要让我们一起对显示的内容,文字系统进行搭建。
目录
一、数据结构抽象
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_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行:打印出具体用法
<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
#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
注意:编译时候要将字库文件一起上传上去
运行效果: