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
#define ___XX_X_ 0x1a
#define ___XX_XX 0x1b
#define ___XXX__ 0x1c
#define ___XXX_X 0x1d
#define ___XXXX_ 0x1e
#define ___XXXXX 0x1f
#define __X_____ 0x20
#define __X____X 0x21
#define __X___X_ 0x22
#define __X___XX 0x23
#define __X__X__ 0x24
#define __X__X_X 0x25
#define __X__XX_ 0x26
#define __X__XXX 0x27
#define __X_X___ 0x28
#define __X_X__X 0x29
#define __X_X_X_ 0x2a
#define __X_X_XX 0x2b
#define __X_XX__ 0x2c
#define __X_XX_X 0x2d
#define __X_XXX_ 0x2e
#define __X_XXXX 0x2f
#define __XX____ 0x30
#define __XX___X 0x31
#define __XX__X_ 0x32
#define __XX__XX 0x33
#define __XX_X__ 0x34
#define __XX_X_X 0x35
#define __XX_XX_ 0x36
#define __XX_XXX 0x37
#define __XXX___ 0x38
#define __XXX__X 0x39
#define __XXX_X_ 0x3a
#define __XXX_XX 0x3b
#define __XXXX__ 0x3c
#define __XXXX_X 0x3d
#define __XXXXX_ 0x3e
#define __XXXXXX 0x3f
#define _X______ 0x40
#define _X_____X 0x41
#define _X____X_ 0x42
#define _X____XX 0x43
#define _X___X__ 0x44
#define _X___X_X 0x45
#define _X___XX_ 0x46
#define _X___XXX 0x47
#define _X__X___ 0x48
#define _X__X__X 0x49
#define _X__X_X_ 0x4a
#define _X__X_XX 0x4b
#define _X__XX__ 0x4c
#define _X__XX_X 0x4d
#define _X__XXX_ 0x4e
#define _X__XXXX 0x4f
#define _X_X____ 0x50
#define _X_X___X 0x51
#define _X_X__X_ 0x52
#define _X_X__XX 0x53
#define _X_X_X__ 0x54
#define _X_X_X_X 0x55
#define _X_X_XX_ 0x56
#define _X_X_XXX 0x57
#define _X_XX___ 0x58
#define _X_XX__X 0x59
#define _X_XX_X_ 0x5a
#define _X_XX_XX 0x5b
#define _X_XXX__ 0x5c
#define _X_XXX_X 0x5d
#define _X_XXXX_ 0x5e
#define _X_XXXXX 0x5f
#define _XX_____ 0x60
#define _XX____X 0x61
#define _XX___X_ 0x62
#define _XX___XX 0x63
#define _XX__X__ 0x64
#define _XX__X_X 0x65
#define _XX__XX_ 0x66
#define _XX__XXX 0x67
#define _XX_X___ 0x68
#define _XX_X__X 0x69
#define _XX_X_X_ 0x6a
#define _XX_X_XX 0x6b
#define _XX_XX__ 0x6c
#define _XX_XX_X 0x6d
#define _XX_XXX_ 0x6e
#define _XX_XXXX 0x6f
#define _XXX____ 0x70
#define _XXX___X 0x71
#define _XXX__X_ 0x72
#define _XXX__XX 0x73
#define _XXX_X__ 0x74
#define _XXX_X_X 0x75
#define _XXX_XX_ 0x76
#define _XXX_XXX 0x77
#define _XXXX___ 0x78
#define _XXXX__X 0x79
#define _XXXX_X_ 0x7a
#define _XXXX_XX 0x7b
#define _XXXXX__ 0x7c
#define _XXXXX_X 0x7d
#define _XXXXXX_ 0x7e
#define _XXXXXXX 0x7f
#define X_______ 0x80
#define X______X 0x81
#define X_____X_ 0x82
#define X_____XX 0x83
#define X____X__ 0x84
#define X____X_X 0x85
#define X____XX_ 0x86
#define X____XXX 0x87
#define X___X___ 0x88
#define X___X__X 0x89
#define X___X_X_ 0x8a
#define X___X_XX 0x8b
#define X___XX__ 0x8c
#define X___XX_X 0x8d
#define X___XXX_ 0x8e
#define X___XXXX 0x8f
#define X__X____ 0x90
#define X__X___X 0x91
#define X__X__X_ 0x92
#define X__X__XX 0x93
#define X__X_X__ 0x94
#define X__X_X_X 0x95
#define X__X_XX_ 0x96
#define X__X_XXX 0x97
#define X__XX___ 0x98
#define X__XX__X 0x99
#define X__XX_X_ 0x9a
#define X__XX_XX 0x9b
#define X__XXX__ 0x9c
#define X__XXX_X 0x9d
#define X__XXXX_ 0x9e
#define X__XXXXX 0x9f
#define X_X_____ 0xa0
#define X_X____X 0xa1
#define X_X___X_ 0xa2
#define X_X___XX 0xa3
#define X_X__X__ 0xa4
#define X_X__X_X 0xa5
#define X_X__XX_ 0xa6
#define X_X__XXX 0xa7
#define X_X_X___ 0xa8
#define X_X_X__X 0xa9
#define X_X_X_X_ 0xaa
#define X_X_X_XX 0xab
#define X_X_XX__ 0xac
#define X_X_XX_X 0xad
#define X_X_XXX_ 0xae
#define X_X_XXXX 0xaf
#define X_XX____ 0xb0
#define X_XX___X 0xb1
#define X_XX__X_ 0xb2
#define X_XX__XX 0xb3
#define X_XX_X__ 0xb4
#define X_XX_X_X 0xb5
#define X_XX_XX_ 0xb6
#define X_XX_XXX 0xb7
#define X_XXX___ 0xb8
#define X_XXX__X 0xb9
#define X_XXX_X_ 0xba
#define X_XXX_XX 0xbb
#define X_XXXX__ 0xbc
#define X_XXXX_X 0xbd
#define X_XXXXX_ 0xbe
#define X_XXXXXX 0xbf
#define XX______ 0xc0
#define XX_____X 0xc1
#define XX____X_ 0xc2
#define XX____XX 0xc3
#define XX___X__ 0xc4
#define XX___X_X 0xc5
#define XX___XX_ 0xc6
#define XX___XXX 0xc7
#define XX__X___ 0xc8
#define XX__X__X 0xc9
#define XX__X_X_ 0xca
#define XX__X_XX 0xcb
#define XX__XX__ 0xcc
#define XX__XX_X 0xcd
#define XX__XXX_ 0xce
#define XX__XXXX 0xcf
#define XX_X____ 0xd0
#define XX_X___X 0xd1
#define XX_X__X_ 0xd2
#define XX_X__XX 0xd3
#define XX_X_X__ 0xd4
#define XX_X_X_X 0xd5
#define XX_X_XX_ 0xd6
#define XX_X_XXX 0xd7
#define XX_XX___ 0xd8
#define XX_XX__X 0xd9
#define XX_XX_X_ 0xda
#define XX_XX_XX 0xdb
#define XX_XXX__ 0xdc
#define XX_XXX_X 0xdd
#define XX_XXXX_ 0xde
#define XX_XXXXX 0xdf
#define XXX_____ 0xe0
#define XXX____X 0xe1
#define XXX___X_ 0xe2
#define XXX___XX 0xe3
#define XXX__X__ 0xe4
#define XXX__X_X 0xe5
#define XXX__XX_ 0xe6
#define XXX__XXX 0xe7
#define XXX_X___ 0xe8
#define XXX_X__X 0xe9
#define XXX_X_X_ 0xea
#define XXX_X_XX 0xeb
#define XXX_XX__ 0xec
#define XXX_XX_X 0xed
#define XXX_XXX_ 0xee
#define XXX_XXXX 0xef
#define XXXX____ 0xf0
#define XXXX___X 0xf1
#define XXXX__X_ 0xf2
#define XXXX__XX 0xf3
#define XXXX_X__ 0xf4
#define XXXX_X_X 0xf5
#define XXXX_XX_ 0xf6
#define XXXX_XXX 0xf7
#define XXXXX___ 0xf8
#define XXXXX__X 0xf9
#define XXXXX_X_ 0xfa
#define XXXXX_XX 0xfb
#define XXXXXX__ 0xfc
#define XXXXXX_X 0xfd
#define XXXXXXX_ 0xfe
#define XXXXXXXX 0xff
那么,“中”字就可以很直观的看出来:
/* 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 的数据结构
uCGUI 有对外公布的字体接口 GUI_FONT ,还有3个内部的数据结构,所以总的数据结构是4个:
1、GUI_FONT 整个字体的结构描述
2、GUI_FONT_PROP 字符信息块的结构
3、GUI_CHARINFO 字符映射的总表
4、字符的图元数据
他们在内存中的组织形式如下:
三、GB2312 和 GBK
通过前面一、二小节内容,我们基本认识了 uCGUI 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)。
注: 《ASCII、Unicode 和 UTF-8 之间的关系》,请详见:
https://www.asciim.cn/m/articles/ascii_unicode_utf8.html
那么如何区分一段文字中的汉字和英文呢?
比如:中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(127=0x7F),那么就认为是2个Byte组成的中文,否则就是1个Byte的英文。
下面使用 Delphi 语言写出该判断算法,源码如下:
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 字符简介
GB2312中对汉字进行了“分区”处理,处理规则为:
- 每区含有
94
个汉字和符号,这种处理方式也称为区位码(区号 位号,一块组成一个区位码); - 每个汉字及符号以
2
个字节来表示; - 第一个字节称为“高位字节”,第二个字节称为“低位字节” (即:大端模式);
- 第一个字节是
区号
,第二个字节叫位号
;
区号 和 位号,均从1开始编号; - 高位字节,使用的十六进制范围:0xA1~0xF7 ,使用的区号范围:01~87。 算法原理:高位字节 = 区号 + 0xA0 ,例如:0xF7=87+0xA0
低位字节,使用的十六进制范围:0xA1~0xFE ,对应的位号范围:01~94。 算法原理:低位字节 = 位号 + 0xA0 ,例如:0xFE=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。)
作者:coscka
博客地址: https://blog.csdn.net/coscka