[Feature phone 系列]字符信息的显示和绘制原理
程序员文章站
2024-01-04 09:50:16
...
这里的内容,不属于完全原创,引用了比部分内容,由于之前整理的时间过早,所以没有记录出处,如有雷同请包涵.
1>首先必须有字库,这里只涉及点阵字库[不同国家,不同大小]
因为点阵字库在嵌入式设备上使用最广。
有点:方便开发,执行效率高;
缺点:通用性差[不能对字体进行缩放等操作].
2>如何添加字库?
首先澄清一些概念:
字符:是各种文字和符号的总称,包括各个国家的文字,标点符号,图形符号,数字等.
字符集:是多个字符的集合,如:ASCII字符集,ISO8859字符集,GB2312-80字符集,BIG5字符集,GB18030字符
集,Unicode字符集等.<字符的集合>
字符编码:规定每个“字符”分别用一个字节还是多个字节存储,用哪些字节来存储,这个规定就叫做“编码”.
当各个国家和地区在制订编码标准的时候,"字符的集合"和"编码"一般都是同时定制的.因此我们平常所说的"字
符集",比如:GB2312等,除了有"字符的集合"这层含义外,同时也包含了"编码"的含义.
内码:*上解释,内码指的是"将资讯编码后,透过某种方式存储在特定的记忆装置时,装置内部的编码形式"。在不
同的系统中,会有不同的内码。比如:GB2312字符集内定义的编码的含义[双字节,四字节],ASCII字符集单字节
编码.
Windows 操作系统上,记事本保存TXT格式的三个选项:
ANSI,American National Standards Institute(ANSI——美国国家标准学会),ASCII字符集[英文系统]/GB2312字符集[简体中文系统]
Unicode,
Unicode big endian,
UTF-8,
首先我们需要根据自己的平台[硬件平台无关,软件平台],当前支持哪种字符编码规范[或者说当前使用的是哪种]
当前可供使用的字符集编码规范:
1>ASCII用来表示英文字符的一种编码规范,每个字符占用一个byte宽度,因此它能表示的最大英文字符数是256.
英文字符并没有这么多因此,一般只用前128个(最高位为0),其中包括了控制字符、数字、大小写字母和其他一些符号.
而最高位为1的另128个字符被成为“扩展ASCII”,一般用来存放英文的制表符、部分音标字符等等的一些其他符号,这种字符编
码规范显然用来处理英文没有什么问题。(实际上也可以用来处理法文、德文等一些其他的西欧字符,但是不能和英文通用),但
是面对中文、阿拉伯文之类复杂的文字,255个字符显然不够用.
2>GB2312-80
GB2312(1980年)一共收录了7445个字符,包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7,低字节从A1-FE,
......
占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE.
由于上面的原因,中文的文字编码规范叫做“GB2312-80”,它是和ASCII兼容的一种编码规范,其实就是利用扩展ASCII没有真正标准
化这一点,把一个中文字符用两个扩展ASCII字符来表示.
问题:
中文文字没有真正属于自己的编码,因为扩展ASCII码虽然没有真正的标准化,但是PC里的ASCII码还是有一个事实标准的
(存放着英文制表符),所以很多软件利用这些符号来画表格。这样的软件用到中文系统中,这些表格符就会被误认作中文字,
破坏版面。而且,统计中英文混合字符串中的字数,也是比较复杂的,我们必须判断一个ASCII码是否扩展,以及它的下一个
ASCII是否扩展,然后才“猜”那可能是一个中文字。
扩展问题:
GB2312是内地国家标准;Big5是*标准,而且同时和GB2312存在一些相同的编码.
所以手机、MP3一般只支持GB2312.
---------------------------------------------------------------------
当中国人们得到计算机时,已经没有可以利用的字节状态来表示汉字,况且有6000多个常用汉字需要保存,于是想到把那些ASCII码中127号之后的奇异符号们直接取消掉,
规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(称之为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了。这种汉字方案叫做 "GB2312"。GB2312
是对 ASCII 的中文扩展。兼容ASCII。
对于人名、古汉语等方面出现的罕用字,GB 2312不能处理,这导致了后来GBK及GB 18030汉字字符集的出现.
分区表示
字节结构GB 2312中对所收汉字进行了“分区”处理,每区含有94个汉字/符号。这种表示方式也称为区位码。01-09区为特殊符号。16-55区为一级汉字,按拼音排序。56-87区为二级汉字,按部首/笔画排序。10-15区及88-94区则未有编码。举例来说,“啊”字是GB2312之中的第一个汉字,它的区位码就是1601。
在使用GB2312的程序通常采用EUC[Extend Unix Code]储存方法,以便兼容于ASCII。浏览器编码表上的“GB2312”,通常都是指“EUC-CN”表示法。每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”,第二个字节称为“低位字节”。“高位字节”使用了0xA1-0xF7(把01-87区的区号加上0xA0),“低位字节”使用了0xA1-0xFE(把01-94加上0xA0)。 由于一级汉字从16区起始,汉字区的“高位字节”的范围是0xB0-0xF7,“低位字节”的范围是0xA1-0xFE,占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。例如“啊”字在大多数程序中,会以两个字节,0xB0(第一个字节)0xA1(第二个字节)储存。(与区位码对比:0xB0=0xA0+16,0xA1=0xA0+1)。
3>Big5
同样由于第一个原因,TW自己也弄了一个标准:Big5其中和GB3212里面存在一些相同的编码.
4>Unicode
Unicode的学名"UniversalMultiple-Octet
Coded Character Set",
前面提到从ASCII、GB2312、GBK到GB18030的编码方法是向下兼容的。而Unicode只与ASCII兼容(更准确地说,是与ISO-8859-1
兼容),与GB码不兼容。例如“汉”字的Unicode编码是6C49,而GB码是BABA。
简称为UCS。UCS可以看作是"Unicode Character Set"的缩写.
很过国家的语言都面临上面的问题,因此要解决这个问题,Unicode编码就诞生了。Unicode有两个标准:
<UCS-2(Unicode-16),用2个字节为字符编码>Unicode
Character Set
以目前常用的UCS-2为例,它可以表示的字符数为2^16=65535,基本上可以容纳所有的欧美字符和绝大部分的亚洲字符。
在Unicode里,所有的字符被一视同仁。汉字不再使用“两个扩展ASCII”,而是使用“1个Unicode”,注意,现在的汉字是
“一个字符”了,于是,拆字、统计字数这些问题也就自然而然的解决了。
问题:
A.兼容ASCII编码
由于Unicode诞生的时候不是所有的系统都使用Unicode来处理字符,因此必须考虑这个严峻的问题:和ASCII字符集之间的不兼容问题。
----ASCII字符是单个字节的,比如“A”的ASCII是65。而Unicode是双字节的,比如“A”的Unicode是0065,这就造成了一个
非常大的问题:以前处理ASCII的那套机制不能被用来处理Unicode了
B.C语言使用'\0'作为字符串结尾,而Unicode里恰恰有很多字符都有一个字节为0,这样一来,C语言的字符串函数将无法正常处
理Unicode,除非把世界上所有用C写的程序以及他们所用的函数库全部换掉.
<UCS-4(Unicode-32),用4个字节为字符编码>
UCS-2、UCS-4、BMP[Basic
Multilingual Plane]
UCS有两种格式:UCS-2和UCS-4。顾名思义,UCS-2就是用两个字节编码,UCS-4就是用4个字节(实际上只用了31位,
最高位必须为0)编码。下面让我们做一些简单的数学游戏:
UCS-2有2^16=65536个码位,UCS-4有2^31=2147483648个码位
UCS-4根据最高位为0的最高字节分成2^7=128个group。每个group再根据次高字节分为256个plane。每个plane根据第3个
字节分为256行 (rows),每行包含256个cells。当然同一行的cells只是最后一个字节不同,其余都相同.
group
0的plane 0被称作Basic Multilingual Plane, 即BMP。或者说UCS-4中,高两个字节为0的码位被称作BMP。
将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字节,就得到了UCS-4的BMP。
而目前的UCS-4规范中还没有任何字符被分配在BMP之外.
5>UTF= UCS Transformation Format
要解决上面的问题,UTF诞生了,UCS转换格式,它是将Unicode编码规则和计算机的实际编码对应起来的一个规则。
现在流行的UTF有2种:UTF-8和UTF-16.
A.UTF-16:
其中UTF-16和上面提到的Unicode本身的编码规范是一致的,因此不用再叙述.
B.UTF-8:
它定义了一种“区间规则”,这种规则可以和ASCII编码保持最大程度的兼容.
UTF-8有点类似于Haffman编码,它将Unicode编码为00000000-0000007F的字符,用单个字节来表示:
00000080-000007FF的字符用两个字节表示
00000800-0000FFFF的字符用3字节表示
因为目前为止Unicode-16规范没有指定FFFF以上的字符,所以UTF-8最多是使用3个字节来表示一个字符。但理论上来说,
UTF-8最多需要用6字节表示一个字符.
在UTF-8里,英文字符仍然跟ASCII编码一样,因此原先的函数库可以继续使用。而中文的编码范围是在0080-07FF之间,
因此是2个字节表示(但这两个字节和GB编码的两个字节是不同的),用专门的Unicode处理类可以对UTF编码进行处理.
PS:
在Unicode出来之前总共有三套中文编码标准:
GB2312-80,是*使用的国家标准,其中一共编码了6763个常用简体汉字。Big5,是*使用的编码标准,编码了*使用
的繁体汉字,大概有8千多个。HKSCS,是中国香港使用的编码标准,字体也是繁体,但跟Big5有所不同.
这3套编码标准都采用了两个扩展ASCII的方法,因此,几套编码互不兼容,而且编码区间也各有不同。
--------------------
UTF-8就是以8位为单元对UCS进行编码。从UCS-2到UTF-8的编码方式如下:
UCS-2编码(16进制)
UTF-8 字节流(二进制)
0000 - 007F 0xxxxxxx0080 - 07FF 110xxxxx 10xxxxxx0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx
例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间,所以肯定要用3字节模板了:1110xxxx
10xxxxxx 10xxxxxx。
将6C49写成二进制是:0110 110001 001001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,
即E6 B1 89.
UTF-16以16位为单元对UCS进行编码。对于小于0x10000的UCS码,UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于
0x10000的UCS码,定义了一个算法。不过由于实际使用的UCS2,或者UCS4的BMP必然小于0x10000,所以就目前而言,可以认为
UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案,UTF-16却要用于实际的传输,所以就不得不考虑字节序的问题.
UTF的字节序和BOM[Byte
Order Mark]
UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元
的字节序。例如收到一个“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这
是“奎”还是“乙”?
Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill
Of Material”的BOM表,而是Byte Order Mark。BOM是一个有
点小聪明的想法:
在UCS编码中有一个叫做"ZERO
WIDTH NO-BREAKSPACE"的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该
出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符"ZEROWIDTH
NO-BREAK SPACE"。
这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符
"ZERO WIDTH NO-BREAK SPACE"又被称作BOM.
UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是
EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所以如果接收者收到以EF BBBF开头的字节流,就知道这是UTF-8编码了.
Windows就是使用BOM来标记文本文件的编码方式的.
6>GBK
1995年的汉字扩展规范GBK1.0收录了21886个符号,它分为汉字区和图形符号区。汉字区包括21003个字符.
国际上又制定了针对中文的统一字符集GBK和GB18030,其中GBK已经在Windows、Linux等多种操作系统中被实现.
GBK兼容GB2312,并增加了大量不常用汉字,还加入了几乎所有的Big5中的繁体汉字。但是GBK中的繁体汉字和Big5中的几乎不兼容.
7>GB18030
GB18030相当于是GBK的超集,比GBK包含的字符更多。据我所知目前还没有操作系统直接支持GB18030.
2000年的GB18030是取代GBK1.0的正式国家标准。该标准收录了27484个汉字,同时还收录了藏文、蒙文、
*文等主要的少数民族文字。现在的PC平台必须支持GB18030,对嵌入式产品暂不作要求.
8>HKSCS
HKSCS,是中国香港使用的编码标准,字体也是繁体,但跟Big5有所不同.
PS:
big
endian和little endian
endian是CPU处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49。那么写到文件里时,究竟是将6C写在前面,
还是将49写在前面?如果将6C写在前面,就是big endian。还是将49写在前面,就是little endian.
字符必须编码后才能被计算机处理。计算机使用的缺省编码方式就是计算机的内码。早期的计算机使用7位的ASCII编码,为了处理汉字,
程序员设计了用于简体中文的GB2312和用于繁体中文的big5.
其实任何平台上都是同样的过程:把font.c文件添加到对应的编译目录下就可以了。
3>系统编译好之后,运行时如何找到并正确显示出来对应的字符?
the font use the unicode Character coding standard[字符编码规范].
u4E00_4E5A_font.h //Just include all font information from 0X4E00~0X4E5A.
//The font information include:
//Ex, 4E00 is the chinese character “一”
bbw=16, bbh=16, bbx=0, bby=0, width=16
//Comments: we can image the screen is big area, a font will take a rectangular
//which have start coordination< x,y >, with and height. the font are a series binary data which 1 is
//showing a point in current screen, 0 is showing nothing on the screen on the 16X16 screen erea.
0x0000, //16 bit size
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0xfffe,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
u4E00_4E5A_font_offset.h //Just include the value of a series font offset from 0X4E00~0X4E5A.
// Ex,
11904, /* (0x4e00) */
.....
0 /* (0x4e5a) */ “乙”
u4E00_4E5A_font_width.h //Just include the value of a series font with
16, /* (0x4e00) */
......
8 /* (0x4e5a) */ “乙”
---------------------------------------
T_FontBmInfo MMI_lcd_get_font_Bm_data(UINT16 wUnicode)
{
UINT8 i = 0;
INT32 wOffset = -1;
INT32 wLocalOffset = -1;
T_FontBmInfo tFontBmInfo;
//Please add the detail comments at here!!
for (i = 0; g_tFontTable[i].bCodeType != 0xFF ; i++)
{ //Please add the detail comments at here!!
if (wUnicode >= g_tFontTable[i].bCodeStart && wUnicode <= g_tFontTable[i].bCodeEnd)
{
//Please add the detail comments at here!!
wOffset = wUnicode - g_tFontTable[i].bCodeStart;
if(g_tFontTable[i].bdwOffset[wOffset]) //check all table until find it.
{
break;
}
else
{ //Please add the detail comments at here!!
wOffset = -1;
}
}
else
{ //Please add the detail comments at here!!
wOffset = -1;
}
}
if (-1 == wOffset)
{
wOffset = 32;
i = 0;
}
wLocalOffset = g_tFontTable[i].bdwOffset[wOffset]-g_tFontTable[i].bdwOffset[0];
tFontBmInfo.bWidth = g_tFontTable[i].baWidth[wOffset];
tFontBmInfo.pwFontBm = (UINT16 *)(&g_tFontTable[i].baFontRowData[wLocalOffset]);
tFontBmInfo.bHeight = LCD_DEFAULT_FONT_HEIGHT;
return tFontBmInfo;
}
4>把找到的字符显示到LCD屏幕上
void MMI_lcd_show_font_Bm(UINT16 wShowPos, T_FontBmInfo *ptFontBmInfo, UINT16 wFwdColor, UINT16 wBackColor)
{
UINT8 bLoopHeight = 0;
UINT8 bLoopWidth = 0;
UINT8 i = 0;
UINT16 wFontword;
UINT16 wOffset = 0;
UINT16 wLcdPointIndex = wShowPos;
T_ALLCDRect tRect;
//write font bitmap to lcd
//Please add the detail comments at here!!
for (bLoopHeight = 0; bLoopHeight < ptFontBmInfo->bHeight; bLoopHeight++)
{ //Please add the detail comments at here!!
for (bLoopWidth = 0; bLoopWidth < ptFontBmInfo->bWidth; bLoopWidth+=16)
{ //Please add the detail comments at here!!
wFontword = ptFontBmInfo->pwFontBm[wOffset];
//Please add the detail comments at here!!
for (i = 0; i < min(16, ptFontBmInfo->bWidth-bLoopWidth); i++)
{ //Please add the detail comments at here!!
if (((wLcdPointIndex) > (AL_PLCD_MAX_WIDTH * AL_PLCD_MAX_HEIGHT - 1)) || (wLcdPointIndex < 0))
{
return;
}
if (wFontword & 0x8000)//Please add the detail comments at here!!
{
g_waLcdPanelBuf[wLcdPointIndex] = wFwdColor;
}
else
{
if (wFwdColor != wBackColor)
{
g_waLcdPanelBuf[wLcdPointIndex] = wBackColor;
}
}
wFontword <<= 1;
wLcdPointIndex++;
}// for (i = 0; i < min(16, ptFontBmInfo->bWidth-bLoopWidth); i++)
wOffset++;
}// for (bLoopWidth = 0; bLoopWidth < ptFontBmInfo->bWidth; bLoopWidth+=16)
wLcdPointIndex += (AL_PLCD_MAX_WIDTH - ptFontBmInfo->bWidth);
}// for (bLoopHeight = 0; bLoopHeight < ptFontBmInfo->bHeight; bLoopHeight++)
//Please add the detail comments at here!!
Mmi_lcd_request_refresh(tRect);
}