UCGUI 字体研究
ucgui是一个被广泛使用的嵌入式界面库,其中内置的字体功能非常强大,你可以为你自己的单片机程序界面制作想要的字体。如何制作自己的字体呢, 可以仔细阅读这篇文章,了解一下 ucgui 字体方面的知识。
一、字体基础知识
计算机的字体,分为点阵字体和矢量字体,本文仅研究点阵字体。
实际上,点阵字体跟图片很像,都是一个个像素点绘出来的。比如中国的“中”字,我们在12x12像素中,对它进行绘制时,像素是这样的:
0000010000000000
0000010000000000
0000010000000000
0111111111000000
0100010001000000
0100010001000000
0100010001000000
0111111111000000
0100010001000000
0000010000000000
0000010000000000
0000010000000000
通过这个像素表,隐约能看到“中”字的样子。
转换成十六进制是一个这样的数组:
0x04,0x00
0x04,0x00
0x04,0x00
0x7f,0xc0
0x44,0x40
0x44,0x40
0x44,0x40
0x7f,0xc0
0x44,0x40
0x04,0x00
0x04,0x00
0x04,0x00
由于这样不太直观,不能一眼看出来这个字符的字形是什么。所以 ucgui 定义了 256 个宏,从 0x00~0xFF,用 “_” 代表为0的 bit, 用 “X” 代表为1的bit;比如 “_xxx__xx” = 01110011 = 0x73.
该宏定义放在 gui.h 大家可以查看一下:
#define ________ 0x0
#define _______X 0x1
#define ______X_ 0x2
#define ______XX 0x3
#define _____X__ 0x4
#define _____X_X 0x5
#define _____XX_ 0x6
#define _____XXX 0x7
#define ____X___ 0x8
#define ____X__X 0x9
#define ____X_X_ 0xa
#define ____X_XX 0xb
#define ____XX__ 0xc
#define ____XX_X 0xd
#define ____XXX_ 0xe
#define ____XXXX 0xf
#define ___X____ 0x10
#define ___X___X 0x11
#define ___X__X_ 0x12
#define ___X__XX 0x13
#define ___X_X__ 0x14
#define ___X_X_X 0x15
#define ___X_XX_ 0x16
#define ___X_XXX 0x17
#define ___XX___ 0x18
#define ___XX__X 0x19
(剩余部分略)
那么,“中”字就可以很直观的看出来:
/* char: 中 code:0xD6D0 */
static GUI_CONST_STORAGE unsigned char acD6D0[24] = {
_____X__,________,
_____X__,________,
_____X__,________,
_XXXXXXX,XX______,
_X___X__,_X______,
_X___X__,_X______,
_X___X__,_X______,
_XXXXXXX,XX______,
_X___X__,_X______,
_____X__,________,
_____X__,________,
_____X__,________
};
二、GUI_FONT 的数据结构
GUI_FONT 是一个字体对外公布的接口。还有3个内部的数据结构,所以总的数据结构是4个:
1、GUI_FONT 整个字体的结构描述
2、GUI_FONT_PROP 字符信息块的结构
3、GUI_CHARINFO 字符映射的总表
4、字符的图元数据
他们在内存中的组织形式如下:
三、GB2312和GBK
通过一、二小节内容,我们基本认识了GUI FONT,那么,如何显示汉字呢?
其实,就是 GUI_FONT_PROP 的起始和结束字符的了,把他们设置为汉字的内码即可。
比如 “中”字,在GB2312里面是 D6D0 那么 GUI_FONT_PROP应该是这样:
static GUI_CONST_STORAGE GUI_FONT_PROP Prop1 = {
0xD6D0, /*start :中*/
0xD6D0, /*end :中, len=1*/
&Cinfo[ 0 ],
(void*)0
};
通常一个汉字占2个字节,一个英文占1个字节(ansi编码时,本文不讨论Unicode和UTF8)。
那么如何区分一段文字中的汉字和英文呢?
比如:中a国b人c
他的编码是:D6 D0 61 B9 FA 62 C8 CB 63
中 = D6 D0
a = 61
国 = B9 FA
b = 62
人 = C8 CB
c = 63
我们通过判断每一个字符,如果该字符>127那么就认为是2个byte组成的中文,否则就是1个byte的英文。
代码如下:
j:=0;
i:=1;
while (i<=len) do
begin
outStr:='';
c:=Byte(s[i]);
if c<128 then //英文
begin
outStr:=Char(c);
//Memo2.Lines.Add(outStr);
try
Form1.StrToImg(outStr,w,h,cInfo[j]);
except
break;
end;
Inc(j);
end else
begin //中文
outStr:=Char(c)+s[i+1];
//Memo2.Lines.Add(outStr);
try
Form1.StrToImg(outStr,w,h,cInfo[j]);
except
break;
end;
//cInfo[j].okwidth := w; //中文下强制为w
Inc(j);
Inc(i);
end;
Inc(i);
Form1.progress:=100*i div len;
Application.ProcessMessages;
end;
那么,GB2312和GBK又有什么关系呢?
可以这么理解,GB2312<=GBK。GBK包含了GB2312的所有字符。其他的都一样。
四、GB2312字符简介
GB 2312中对所收汉字进行了“分区”处理,每区含有94个汉字/符号。这种表示方式也称为区位码。
每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”,第二个字节称为“低位字节”。
第一字节是区号,第二字节叫位号。
高位字节,使用了 0xA1~0xF7 即 01–87区,即:高位字节= 区号+0xA0
低位字节,使用了0xA1~0xFE 即 01–94区,即:低位字节=位号+0xA0
例如,“啊” = 0xB0A1
换算成区位:
区 = 0xB0 - 0xA0 = 16
位 = 0xA1 - 0xA0 = 1
即 16 区 1 位。
五、如何生成ucgui字体
使用ucgui字体生成器,可以自动生成想要的字体:
六、ucgui抗锯齿字体
UCGUI是支持抗锯齿字体的,实际上就是把字体灰度化了,ucgui支持4级和16级灰度抗锯齿。
4级灰度叫 GUI_FONTTYPE_AA2_XXXX, 16级灰度叫 GUI_FONTTYPE_AA4_XXXX
点击 AA2(4级灰度)或者 AA4(16级灰度),再按开始转换,即可生成带灰度的字体。
七、把图片转换成字体
虽然 UCGUI 支持图片,如果把图片用来做一些小的 icon 的话,就不如当成字体使用灵活了,当成字体使用的话,可以随时修改 icon 的颜色,因为如果仅仅当成图片使用的话,一种颜色又得换一张图片,就有点太耗存储了。(当然也视情况而定,图片小而个数多时,把图片转字体更灵活;图片大而个数少时,还是当成图片使用;毕竟图片还有压缩模式可以节约ROM。)
八、以上工具均已开源,需要的可自行下载
[1] GB2312码表
https://download.csdn.net/download/coscka/11446816
[2] UCGUI GB2312字体生成器源码
https://download.csdn.net/download/coscka/11302846