26. 如何制作字库

熟悉字符的编码后,我们学习如何制作字库文件。emXGUI支持的字库文件为*.xft类型的文件,可以使用XBF字体制作工具X_GUI_SIM.exe(位于过程目录下\ \emxguitools),这里我们以simfang.ttf为例。

图 26‑1 文件目录

图 26‑1 文件目录

26.1. 工具介绍

图 26‑2 工具界面图

图 26‑2 工具界面图

运行X_GUI_SIM.exe程序,会得到 图26_2 的界面图片。具体操作步骤:

  1. 选择字体文件

这里我们以simfang.ttf文件为例,该文件存放在XBF字体制作工具font目录下,见 图26_3 的处。单击处按键,选择simfang.ttf,如 图26_3 的处所示。

图 26‑3 选择字体文件

图 26‑3 选择字体文件

  1. 生成的字体范围

假如现在只需要使用字母Q,W,E,R,我们可以只生成一个只含有字母Q,W,E,R的xft文件,具体做法:新建一个txt文件,内容为 QWER。注意,Q前面有一个空格,一般情况下,会保留空格的编码。另外,索引文件要使用ucs-2_little_edition编码。我们可以使用Notepad++来进行设置, .如 图26_5 。最后在 图26_2 的处选择刚刚新建的txt文件。

本章使用的simfang.ttf,这里以所有的字符编码为例,其txt文档(位于XBF字体制作工具font目录下),如 图26_4 所示,包含了所有的数字,字母还有中文等编码

图 26‑4 txt文档内容

图 26‑4 txt文档内容

图 26‑5设置编码

图 26‑5设置编码

单击处的按键,选择生成的字体范围,如 图26_6

图 26‑6 选择生成的字体范围

图 26‑6 选择生成的字体范围

  1. 选择输出路径

点击 图26_7 的 1 处,选择输出的xft文件的保存路径,注意,在处输入文件名时,需要手动输入. xft后缀。

图 26‑7 选择路径

图 26‑7 选择路径

  1. 设置输出的格式

点击 图26_8 的 1 处,弹出选项框Setting。处用来设置字体的大小,处主要是设置字体抗锯齿度,emXGUI最高可提供8 BPP,我们通常选择4BPP,处都是些默认参数,不需要修改。Xoff和Yoff负责字体的偏移。DPI代表图像每英寸面积内的像素点数。

图 26‑8 选择字体格式

图 26‑8 选择字体格式

这里解释一下什么叫抗锯齿?在这之前,需要先知道什么叫锯齿现象。图26_9 是图像的原始尺寸。当图像放大时,由于图像的像素点与图像的尺寸是相关的,因此就出现了锯齿现象,类似于楼梯的形状,如 图26_10 的下半部分。

图 26‑9 原始尺寸“印刷”

图 26‑9 原始尺寸“印刷”

图 26‑10 放大的“印刷”

图 26‑10 放大的“印刷”

抗锯齿是一种消除图物边缘出现凹凸锯齿的技术,就是将图像边缘及其两侧的像素颜色进行混合得到的新点,用来替换原来位置上的点以达到平滑边缘,达到消除锯齿的效果。抗锯齿程度越高 ,计算时间越长,但是效果越好。如 图26_11 的下半部分,这是设置抗锯齿(边缘平滑化)之后放大的效果图。

图 26‑11 抗锯齿效果图

图 26‑11 抗锯齿效果图

  1. 开始生成

经过前四步的设置之后,单击 图26_2 的 5 处,就可以生成xft文件了,操作成功的结果,如图 26‑12所示。在输出文件夹下,会有一个新的xft文件。这里我选择的输出路径是XBF字体制作工具Out中,xft文件的名字为wildfire.xtf,注意输入xft文件时,一定要自己输入xft后缀。

图 26‑12 txt文档内容

图 26‑12 生成结果

  1. 测试生成的字体

X_GUI_SIM.exe提供测试生成字体的功能,使用方法如 图26_13 所示。单击 图26_13 处的Test按键,在弹出的对话框中选择刚刚生成的xft文件。这里我们选择的上面生成的wildfire. xft文件。测试的结果如 图26_14 所示,读者可以和 图26_2 对比一下。

图 26‑13 测试方法

图 26‑13 测试方法

图 26‑14测试结果

图 26‑14测试结果

最后,将我们生成的xft文件,放到我们的SD卡中,如 图26_15 。这里统一放到根目录下的文件夹srcdata中。读者也可以选择其他的文件夹,但是程序需要进行修改,建议初学者依葫芦画瓢。

图 26‑15 添加字库文件至SD卡中

图 26‑15 添加字库文件至SD卡中

到这里为止,就完成字库的制作,接下来,我们看一下如何在emXGUI使用我们刚刚生成的字库。

26.2. 使用字库

26.2.1. 相关函数API

26.2.1.1. SetFont

SetFont函数用来设置显示的字体格式,我们通过调用这个函数,可以切换至我们生成的字体,函数原型见 代码清单26_1 SetFont函数(文件emXGUI.h)。

代码清单 26‑1 SetFont函数(文件emXGUI.h)
1
 HFONT SetFont(HDC hdc,HFONT hFont);
  1. hdc:绘图上下文;

  2. hFont:字体句柄。字体句柄是通过XFT_CreateFontEx函数来创建;

此外,该函数还会返回旧的字体句柄,我们可以利用这个返回值,进行两种字体的切换。

26.2.1.2. GetFont

调用GetFont函数会返回当前正在使用的字体,函数原型,见代码清单 26‑2 GetFont函数(文件emXGUI.h)。

代码清单 26‑2 GetFont函数(文件emXGUI.h)
1
 HFONT GetFont(HDC hdc);
  1. hdc:绘图上下文;

26.2.1.3. XFT_CreateFont

代码清单 26‑3 XFT_CreateFont函数(文件GUI_Font_XFT.h)
1
 HFONT XFT_CreateFont(const void *xft_dat);
  1. xft_dat:字体数据,可以使用bin2c工具可以将字库文件(.xtf后缀文件)生成C语言的数组,可以放在内部FLASH中。

26.2.1.4. font_read_data_SDCARD

font_read_data_SDCARD函数从SD卡读取字库文件,得到的字体数组存放在buf中。

代码清单 26‑4 font_read_data_SDCARD函数(文件gui_font_port.c)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
BOOL font_read_data_SDCARD(char** buf, u32 size)

 {
    /* file objects */
    FIL *file;
    FRESULT fresult;
    BOOL result = TRUE;
    UINT br;
    file =(FIL*)GUI_VMEM_Alloc(sizeof(FIL));

    fresult = f_open(file, GUI_DEFAULT_SDCARD_FONT, FA_OPEN_EXISTING | FA_READ );

    size = f_size(file);
    /* 文件内容空间 */
    *buf = (char *)GUI_VMEM_Alloc(size);
    fresult = f_read(file, *buf, size, &br);
    /* 关闭文件 */
    f_close(file);

    /* 释放空间 */
    GUI_VMEM_Free(file);

    return result;
 }

f_open函数的参数GUI_DEFAULT_SDCARD_FONT是我们的字库文件名字,见 代码清单26_5。如果我们存放的字库文件不是这个文件的话, 则需要修改该宏定义。

代码清单26_5 GUI_DEFAULT_SDCARD_FONT宏定义(文件gui_drv_cfg.h)
1
 #define GUI_DEFAULT_SDCARD_FONT "0:srcdata/GB2312_16_4BPP.xft"

调用f_read函数,读取字库文件数据,需要读取的数据大小,为整个文件的的大小,通过f_size函数可以得到文件的大小。最后f_close函数关闭文件,同时释放文件句柄的空间。

26.2.2. 使用字库实验

26.2.2.1. 实验要求

本章节的例程代码与Textout类似,区别在于WM_PAINT消息的处理:在一个窗口中,同一个文本,使用两种字体进行显示,如 图26_16 实验要求。

图 26‑16 实验要求

图 26‑16 实验要求

26.2.2.2. 代码分析

  1. 创建字体句柄

代码清单 26‑6 GUI_Default_FontInit (文件gui_font_port.c)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 HFONT GUI_Default_FontInit(void)
 {

    HFONT hFont=NULL;
    //此处省略一些代码
#elif (GUI_USE_SDCARD_FONT)
    {
    /* 指向缓冲区的指针 */
    static u8 *pFontData_XFT=NULL;

    u32 fsize;

    if(hFont==NULL)
    {
        res = font_read_data_SDCARD((char **)&pFontData_XFT, fsize);
        hFont_SDCARD = XFT_CreateFont(pFontData_XFT);
    }

    }
#endif
    /* 若前面的字体加载失败,使用内部FLASH中的数据(工程中的C语言数组)
    * 添加字体数据时,把数组文件添加到工程,在本文件头添加相应字体数组的声明,
    * 然后调用XFT_CreateFont函数创建字体即可
    */
    if(hFont==NULL)
    {
        /* 从本地加载(本地数组数据) */
        hFont =XFT_CreateFont(GUI_DEFAULT_FONT); /* ASCii字库,20x20,4BPP抗锯齿*/
        /* 中文字库存储占用空间非常大,不推荐放在内部FLASH */
        //hFont =XFT_CreateFont(GB2312_16_2BPP); /* GB2312字库,16x16,2BPP抗锯齿*/
        //hFont =XFT_CreateFont(GB2312_20_4BPP); /* GB2312字库,20x20,4BPP抗锯齿*/
    }
    return hFont;
 }

这里使用了条件编译,只有打开GUI_USE_SDCARD_FONT这个宏,见 代码清单26_7,emXGUI才会从SD卡读取字库文件。

代码清单 26_7 GUI_USE_SDCARD_FONT宏定义(文件gui_drv_cfg.h)
1
 #define GUI_USE_SDCARD_FONT 1

例程中创建了两种字体句柄,见 代码清单26_8 。默认的字体是由XFT_CreateFont函数创建的,而字体wildfire是由font_read_data_SDCARD函数从SD卡中读取的字库文件之后,调用XFT_CreateFont函数创建。

代码清单 26_8 字体类型(文件gui_drv_cfg.h)
1
2
3
4
5
 #define GUI_DEFAULT_SDCARD_FONT "0:srcdata/wildfire.xft"

 /* 默认内部字体数组名,USE_EXTERN_FONT为0或 外部字体加载失败时会采用的字体 */

 #define GUI_DEFAULT_FONT ASCII_20_4BPP
  1. 窗口回调函数

  • WM_PAINT

代码清单 26‑9 WM_PAINT消息(文件GUI_DEMO_TextOut.c)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
 case WM_PAINT: //窗口需要绘制时,会自动产生该消息.
 {
    PAINTSTRUCT ps;
    HDC hdc;
    RECT rc;
    int i,t,y;
    WCHAR wbuf[128];

    GetClientRect(hwnd,&rc);

    hdc =BeginPaint(hwnd,&ps); //开始绘图

    ////用户的绘制内容...
    SetTextColor(hdc,MapRGB(hdc,10,10,100));
    t=GUI_GetTickCount();
    y=24;
    i=0;
    while(y<rc.h)
    {
        if(i == 11)//11行的后面使用wildfire字体文件
        {
            old_hfont = SetFont(hdc, hFont_SDCARD);
        }
        TextOut(hdc,10,y,L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",-1);
        y+=20;
        i++;
    }
    t =GUI_GetTickCount()-t;

    SetTextColor(hdc,MapRGB(hdc,250,10,10));
    SetFont(hdc, old_hfont);
    if(rc.w < 300)
    {
        x_wsprintf(wbuf,L"Time:%dms; %.1fms/line",t,(float)t/(float)i);
    }
    else
    {
        x_wsprintf(wbuf,L"TextOut Time used:%dms; %.1fms/line",t,(float)t/(float)i);
    }
    TextOut(hdc,10,4,wbuf,-1);
    EndPaint(hwnd,&ps); //结束绘图
    break;
 }

WM_PAINT消息中,在客户区显示多行文字,内容为: “0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ”,使用宽字符型字符串。 我们使用的字体高是20,所以使每行的文本的起点y坐标偏移20个像素。前11行,使用默认字体(ASCII_20_4BPP)显示文字, 后面的所有行,使用wildfire的字体格式。调用SetFont函数,hFont_SDCARD作为参数,实现字体的切换,同时将旧字体句柄存放在old_hfont中。 显示消耗时间的文字使用默认字体进行显示,也就是调用SetFont函数,字体句柄选择旧字体句柄old_hfont。这样就可以实现一个窗口有两种文字格式的显示。

最后,将GUI_DEMO_TextOutEX函数添加到GUI_AppMain函数中即可。

26.2.2.3. 实验结果

实验结果如 图26_17 实验结果所示,图26_17 实验结果的 1 处使用的是ASCII_20_4BPP字体,2 处使用的是wildfire字体。

图 26‑17 实验结果

图 26‑17 实验结果