点阵字库的显示原理

在嵌入式开发中,经常会用到字库。字库分为矢量字库和点阵字库,毋庸置疑,矢量字库的显示效果是最好的,但是点阵字库是免费的。所以在很多场合,点阵字库仍然是产品开发的首选。

    点阵从本质上讲就是单色位图,它使用一个比特来表示一个点,如果这个比特为0,表示某个位置没有点,如果为1表示某个位置有点。矩阵和位图有着密不可分的联系,矩阵其实是位图的数学抽象,是一个二维的阵列。位图就是这种二维的阵列,这个阵列中的(x,y)位置上的数据代表的就是对原始图形进行采样量化后的颜色值。但是,另一方面,我们要面对的问题是,计算机中数据的存放都是一维的,线性的。因此,我们需要将二维的数据线性化到一维里面去。通常的做法就是将二维数据按行顺序的存放,这样就线性化到了一维。

    那么点阵字的数据存放细节到底是怎么样的呢。其实也十分的简单,举个例子最能说明问题。比如说 16*16 的点阵,也就是说每一行有16个点,由于一个点使用一个比特来表示,如果这个比特的值为1,则表示这个位置有点,如果这个比特的值为0,则表示这个位置没有点,那么一行也就需要16个比特,而8个比特就是一个字节,也就是说,这个点阵中,一行的数据需要两个字节来存放。

    第一行的前八个点的数据存放在点阵数据的第一个字节里面,第一行的后面八个点的数据存放在点阵数据的第二个字节里面,第二行的前八个点的数据存放在点阵数据的第三个字节里面...,然后后面的就以此类推了。这样我们可以计算出存放一个点阵总共需要32个字节(2*16)。看看下面这个图形化的例子:

               | |1| | | | | | | | | | |1| | | |
               | | |1|1| |1|1|1|1|1|1|1|1|1| | |
               | | | |1| | | | | | | | |1| | | |
               |1| | | | | |1| | | | | |1| | | |
               | |1|1| | | |1| | | | | |1| | | |
               | | |1| | | |1| | | | |1| | | | |
               | | | | |1| | |1| | | |1| | | | |
               | | | |1| | | |1| | |1| | | | | |
               | | |1| | | | | |1| |1| | | | | |
               |1|1|1| | | | | | |1| | | | | | |
               | | |1| | | | | |1| |1| | | | | |
               | | |1| | | | |1| | | |1| | | | |
               | | |1| | | |1| | | | | |1| | | |
               | | |1| | |1| | | | | | |1|1|1| |
               | | | | |1| | | | | | | | |1| | |
               | | | | | | | | | | | | | | | | |

 

    可以看出这是一个“汉”字的点阵,当然文本的方式效果不是很好。根据上面的原则,我们可以写出这个点阵的点阵数据:0x40,0x08,0x37,0xfc,0x10,0x08,..., 当然写这个确实很麻烦所以我不再继续下去。我这样做,也只是为了向你说明,在点阵字库中,每一个点阵的数据就是按照这种方式存放的。

    当然也存在着不规则的点阵,这里说的不规则,指的是点阵的宽度不是8的倍数,比如 12*12 的点阵,那么这样的点阵数据又是如何存放的呢?其实也很简单,每一行的前面8个点存放在一个字节里面,每一行的剩下的4点就使用一个字节来存放,也就是说剩下的4个点将占用一个字节的高4位,而这个字节的低4位没有使用,全部都默认的为零。这样做当然显得有点浪费,不过却能够便于我们进行存放和寻址。对于其他不规则的点阵,也是按照这个原则进行处理的。这样我们可以得出一个 m*n 的点阵所占用的字节数为 (m+7)/8*n。

    在明白了以上所讲的以后,我们可以写出一个显示一个任意大小的点阵字模的函数,这个函数的功能是输出一个宽度为w,高度为h的字模到屏幕的 (x,y) 坐标出,文字的颜色为 color,文字的点阵数据为 pdata,直接指向一个字模的第一个字节。

 


void _draw_model(char *pdata, int w, int h, int x, int y, int color)
{
    int     i;   
    int     j;   
    int     k;   
    int     nc;  
    int     cols;
    w = (w + 7) / 8 * 8;
    nc = 0;

    for (i=0; i<h; i++)
    {
        cols = 0; 
        for (k=0; k<w/8; k++)
        {
            for (j=0; j<8; j++)
            {
                if (pdata[nc]& (0x80 >> j))  //提取对应位
                    putpixel(x+cols, y+i, color);
                cols++;
            }
            nc++;
        }
    }
}

    接下来的问题就是如何在汉字字库中寻址某个汉字的点阵数据了。要解决这个问题,首先需要了解汉字在计算机中是如何表示的。在计算机中英文可以使用 ASCII 码来表示,而汉字使用的是扩展ASCII 码,并且使用两个扩展 ASCII 码来表示一个汉字。一个ASCII 码使用一个字节表示,所谓扩展ASCII 码,也就是 ASCII 码的最高位是1的 ASCII 码,简单的说就是码值大于等于 128 的 ASCII 码。一个汉字由两个扩展 ASCII 码组成,第一个扩展 ASCII 码用来存放区码,第二个扩展 ASCII 码用来存放位码。在 GB2312-80 标准中,将所有的汉字分为94个区,每个区有94个位可以存放94个汉字,形成了人们常说的区位码,这样总共就有 94*94=8836 个汉字。在点阵字库中,汉字点阵数据就是按照这个区位的顺序来存放的,也就是最先存放的是第一个区的汉字点阵数据,在每一个区中又是按照位的顺序来存放的。在汉字的内码中,汉字区位码的存放是在扩展 ASCII 基础上存放的,并且将区码和位码都加上了32,然后存放在两个扩展 ASCII 码中。具体的说就是:
    第一个扩展ASCII码 = 128 + 32 + 汉字区码
    第二个扩展ASCII吗 = 128 + 32 + 汉字位码
    如果用char hz[2]来表示一个汉字,那么我可以计算出这个汉字的区位码为:
    区码 = hz[0] - 128 - 32 = hz[0] - 160
    位码 = hz[1] - 128 - 32 = hz[1] - 160。

    这样,我们可以根据区位码在文件中进行寻址了,寻址公式如下:
    汉字点阵数据在字库文件中的偏移 = ((区码-1) * 94 + 位码 - 1) * 一个点阵字模占用的字节数
在寻址以后,即可读取汉字的点阵数据到缓冲区进行显示了。以下是实现代码:

   


void _draw_hz(char hz[2], FILE *fp, int x, int y, int w, int h, int color)
{
    char fontbuf[128];  
    int ch0 = (BYTE)hz[0]-0xA0;
    int ch1 = (BYTE)hz[1]-0xA0;

   
    long offset = (long)pf->_hz_buf_size * ((ch0 - 1) * 94 + ch1 - 1);

    fseek(fp, offset, SEEK_SET);             
    fread(fontbuf, 1, (w + 7) / 8 * h, fp);  
    _draw_model(fontbuf, w, h, x, y, color);
}

    以上介绍完了中文点阵字库的原理,当然还有英文点阵字库了。英文点阵字库中单个点阵字模数据的存放方式与中文是一模一样的,也就是对我们所写的 _draw_model 函数同样可以使用到英文字库中。唯一不同的是对点阵字库的寻址上。英文使用的就是 ASCII 码,其码值是0到127,寻址公式为:
    英文点阵数据在英文点阵字库中的偏移 = 英文的ASCII码 * 一个英文字模占用的字节数。

 

 

简单的应用例程:

void drawChineseTextString(char *textStr, FILE *fp, int x, int y, int w, int h, int color)
{

    char * str;
    int i;


    i=0;
    str=textStr;
 
   while(*str!=0)
   {
       _draw_hz(str,fp,x+i*w,y,w,h,color);
       str+=2;
        i++;
    }

/

*****************

GB2312-80字库文件假设为/home目录下的1616.hzk,我们用的是16*16的点阵:

******************

/

int main()

{
    char * hz="这是一个测试";
    FILE *fp = fopen("/home/1616.hzk", "r");
    drawChineseTextString(hz,fp,200,200,16,16,30);
}

注意:编译的时候,源文件也要选择为GB2312-80格式的,否则可能显示不正常。

 

可以通过专门的软件来生成字库。

例如,http://www.onlinedown.net/softdown/25211_2.htm

 

依据编码形式:
GB2312-80 编码为 2个字节(Byte) 包含了 20902 个汉字,其编码范围是 0x8140-0xfefe。
GB18030-2000(GBK2K) 在 GBK 的基础上进一步扩展了汉字,增加了藏、蒙等少数民族的字形。编码是变长的,其二字节部分与 GBK 兼容;四字节部分是扩充的字形、字位,其编码范围是首字节 0x81-0xfe、二字节0x30-0x39、三字节 0x81-0xfe、四字节0x30-0x39
Unicode 范围一般所用为\U0000-\UFFFF,对于CJK EXT B区汉字,范围大于\U20000
UTF, 按其基本长度所用位数分为UTF-8/16/32。其中:
UTF-8是变长编码,每个Unicode代码点按照不同范围,可以有1-3字节的不同长度。

UTF-16长度相对固定,只要不处理大于\U200000范围的字符,每个Unicode代码点使用16位即2字节表示,超出部分使用两个UTF-16即4字节表示。按照高低位字节顺序,又分为UTF-16BE/UTF-16LE。

UTF-32长度始终固定,每个Unicode代码点使用32位即4字节表示。按照高低位字节顺序,又分为UTF-32BE/UTF-32LE。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值