跳到主要内容位置

文字系统

1.数据结构抽象#

描述字符:位置、大小、点阵

文字的点阵可以从固定大小的点阵字库得到,也可以从freetype这类矢量字库得到。

所以后一个字符的位置会受到前一个字符的影响。

/* 字符位图描述 */
typedef struct FontBitMap
{
int iLeftUpX;
int iLeftUpY;
int iWidth;
int iRows;
int iCurOriginX; /* 当前字符的基点 */
int iCurOriginY;
int iNextOriginX; /* 下一个字符的基点 */
int iNextOriginY;
unsigned char *pucBuffer; //点阵指针
} FontBitMap, *PFontBitMap;

为给定字符,得到和操作FontBitMap,还需要构造一个结构体:

typedef struct FontOpr
{
char *name;
int (*FontInit)(char *aFontName);
int (*SetFontSize)(int iFontSize);
int (*GetFontBitMap)(unsigned int dwCode, PFontBitMap ptFontBitMap);
struct FontOpr *ptNext;
} FontOpr, *PFontOpr;

2.freetype#

Freetype 是开源的字体引擎库,它提供统一的接口来访问多种字体格式文件,从而实现矢量字体显示。

我们编写freetype.c调用freetype库。

编程:

先修改一下FontBitMap结构体:实用与显示系统一样的Region结构体,且将Region结构体放入common.h,避免disp_manager.hfont_manager.h的相互包含错误。

typedef struct FontBitMap
{
// int iLeftUpX; /* 位图左上角坐标 */
// int iLeftUpY;
// int iWidth; /* 宽度 */
// int iRows; /* 行数 */
Region tRegion;
int iCurOriginX; /* 当前字符的基点 */
int iCurOriginY;
int iNextOriginX; /* 下一个字符的基点 */
int iNextOriginY;
unsigned char *pucBuffer; //点阵指针
} FontBitMap, *PFontBitMap;

然后实现三个函数,定义g_tFreetypeOpr结构体:

  • FreetypeFontInit: 初始化freetype,加载字体文件,保存在&g_tFace中,设置一个默认的字体大小
  • FreetypeSetFontSize:设置字体大小FT_Set_Pixel_Sizes
  • FreetypeGetFontBitMap:先确定坐标原点为当前原点,FT_Set_Transform。根据编码值得到位图,使用 FT_Load_Char 函数
static FT_Face g_tFace;
static int g_iDefaultFontSize = 12; //默认字体大小
static int FreetypeFontInit(char *aFontName)
{
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, aFontName, 0, &g_tFace); /* create face object 加载字体文件,保存在&g_tFace */
if (error)
{
printf("FT_Init_FreeType 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; //起笔点,与origin point单位不同
FT_Glyph glyph;
FT_GlyphSlot slot = g_tFace->glyph;
/* 反推原点 */
pen.x = ptFontBitMap->iCurOriginX * 64; /* 单位: 1/64像素 */
pen.y = ptFontBitMap->iCurOriginY * 64; /* 单位: 1/64像素 */
FT_Set_Transform(g_tFace, 0, &pen); /* 转换:transformation */
/* 加载位图: 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->iLeftUpX = slot->bitmap_left;
ptFontBitMap->iLeftUpY = ptFontBitMap->iCurOriginY * 2 - slot->bitmap_top; // 转换为LCD坐标
ptFontBitMap->iWidth = slot->bitmap.width;
ptFontBitMap->iRows = slot->bitmap.rows;
ptFontBitMap->iNextOriginX = ptFontBitMap->iCurOriginX + slot->advance.x / 64; /* 下一个字符的基点,x = curx + */
ptFontBitMap->iNextOriginY = ptFontBitMap->iCurOriginY; // y值不变
}
static FontOpr g_tFreetypeOpr = {
.name = "freetype",
.FontInit = FreetypeFontInit,
.SetFontSize = FreetypeSetFontSize,
.GetFontBitMap = FreetypeGetFontBitMap,
};

ptFontBitMap->iLeftUpY需要将位图坐标(笛卡尔坐标)转换到LCD坐标:

3.文字管理#

#include <string.h>
#include "font_manager.h"
static PFontOpr g_ptFont = NULL; // font链表头
static PFontOpr g_ptDefaultFontOpr = NULL;
/* 下层调用 */
void RegisterFont(PFontOpr ptFontOpr)
{
ptFontOpr->ptNext = g_ptFont;
g_ptFont = ptFontOpr;
}
/* 上层调用,注册所有字体 */
void FontsRegister(void)
{
extern void FreetypeRegister(void);
FreetypeRegister();
}
/* SelectAndInitFont("freetype", "simsun.ttc") */
int SelectAndInitFont(char *aFontOprName, char *aFontFileName)
{
PFontOpr ptTmp = g_ptFont;
while (ptTmp)
{
if (strcmp(ptTmp->name, aFontOprName) == 0)
break;
ptTmp = ptTmp->ptNext;
}
if (!ptTmp)
return -1;
g_ptDefaultFontOpr = ptTmp;
return ptTmp->FontInit(aFontFileName);
}
int SetFontSize(int iFontSize)
{
return g_ptDefaultFontOpr->SetFontSize(iFontSize);
}
int GetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)
{
return g_ptDefaultFontOpr->GetFontBitMap(dwCode, ptFontBitMap);
}

4.测试#

编译#

(1)修改makefile

在font子目录下

EXTRA_CFLAGS :=
CFLAGS_file.o :=
obj-y += font_manager.o
obj-y += freetype.o

(2)相互包含问题:将Region结构体放入common.h中,disp和font都可以使用

(3)freetype库

LDFLAGS := -lts -lpthread -lfreetype
...
obj-y += unittest/
obj-y += display/
obj-y += input/
obj-y += font/

(4)交叉编译freetype方法

一定要先设置交叉编译工具链!!!不然编译出来的默认是X86架构的程序

参考《嵌入式Linux应用开发完全手册》6.4.4给IMX6ULL交叉编译freetype

确定库的位置:

echo 'main(){}'| arm-buildroot-linux-gnueabihf-gcc -E -v -
/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/include
/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/../../../../arm-buildroot-linux-gnueabihf/lib/

编译libpng:

book@100ask:~/libpng-1.6.37$ ./configure --host=arm-buildroot-linux-gnueabihf --prefix=$PWD/tmp
book@100ask:~/libpng-1.6.37$ make
book@100ask:~/libpng-1.6.37$ make install
book@100ask:~/libpng-1.6.37$ cd tmp
book@100ask:~/libpng-1.6.37/tmp$ cp include/* -rf /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/include
book@100ask:~/libpng-1.6.37/tmp$ cp lib/* -rfd /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/../../../../arm-buildroot-linux-gnueabihf/lib

编译freetype

book@100ask:~/freetype-2.10.2$ ./configure --host=arm-buildroot-linux-gnueabihf --prefix=$PWD/tmp
book@100ask:~/freetype-2.10.2$ make
book@100ask:~/freetype-2.10.2$ make install
book@100ask:~/freetype-2.10.2$ cd tmp
book@100ask:~/freetype-2.10.2/tmp$ cp include/* -rf /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/include
book@100ask:~/freetype-2.10.2/tmp$ cp lib/* -rfd /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/lib/

上机测试#

font_test.c:

  • 初始化显示系统,获取显存buffer

    DisplayInit();
    SelectDefaultDisplay("fb");
    InitDefaultDisplay();
    ptBuffer = GetDisplayBuffer(); //获得显存buffer
  • 注册所有Fonts,选择freetype字体库,字体文件通过argv[1]传入

    FontsRegister();
    err = SelectAndInitFont("freetype", argv[1]);
    if (err)
    {
    printf("SelectAndInitFont err\n");
    return -1;
    }
  • 设置字体大小

    SetFontSize(font_size);
  • 循环遍历字符串,获取位图,绘制位图

    while (str[i])
    {
    tFontBitMap.iCurOriginX = lcd_x;
    tFontBitMap.iCurOriginY = lcd_y;
    err = GetFontBitMap(str[i], &tFontBitMap);
    if (err)
    {
    printf("%c GetFontBitMap err\n", str[i]);
    return -1;
    }
    /* 绘制位图 */
    DrawFontBitMap(&tFontBitMap, 0xff0000);
    FlushDispalyRegion(&tFontBitMap.tRegion, ptBuffer);
    /* 更新 */
    lcd_x = tFontBitMap.iNextOriginX;
    lcd_y = tFontBitMap.iNextOriginY;
    i++;
    }

    DrawFontBitMap函数在disp_manager.c中实现:

    基本方法就是遍历位图buffer中的每一个像素点,如果不等于0就绘制一个像素点,等于0就跳过不绘制。

    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.iHight;
    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_tDisplay.iXres || j >= g_tDisplay.iYres)
    continue;
    if (buffer[q * width + p] != 0)
    PutPixel(i, j, dwColor);
    }
    }
    }

主函数完整代码

int main(int argc, char **argv)
{
PDispBuff ptBuffer;
int err;
int i = 0;
FontBitMap tFontBitMap;
char *str = "www.100ask.net";
int lcd_x, lcd_y, 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(); //获得显存buffer
FontsRegister();
err = SelectAndInitFont("freetype", argv[1]);
if (err)
{
printf("SelectAndInitFont err\n");
return -1;
}
SetFontSize(font_size);
while (str[i])
{
tFontBitMap.iCurOriginX = lcd_x;
tFontBitMap.iCurOriginY = lcd_y;
err = GetFontBitMap(str[i], &tFontBitMap);
if (err)
{
printf("%c GetFontBitMap err\n", str[i]);
return -1;
}
/* 绘制位图 */
DrawFontBitMap(&tFontBitMap, 0xff0000);
FlushDispalyRegion(&tFontBitMap.tRegion, ptBuffer);
/* 更新 */
lcd_x = tFontBitMap.iNextOriginX;
lcd_y = tFontBitMap.iNextOriginY;
i++;
}
return 0;
}

错误if (buffer[q * width + p] == 1) 不一定是等于1,正确的逻辑应该是不等于零的点就绘制像素,等于0的点跳过

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.iHight;
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_tDisplay.iXres || j >= g_tDisplay.iYres)
continue;
if (buffer[q * width + p] == 1) //错误
PutPixel(i, j, dwColor);
}
}
}

请点击左侧菜单(移动端为右下角)选择要查看的所有笔记吧。