
正文
点阵字体显示系列之一:ASCII码字库的显示
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
http://blog.csdn.net/subfate/article/details/6444578
起因:
早在阅读tslib源代码时就注意到里面有font_8x8.c和font_8x16.c两个文件(后来才得知,它们来自Linux内核,具体目录是./drivers/video/console),它们包含了在屏幕上显示所需的所有字符(当然,是可打印字符),由此也看了一下tslib显示字符的代码。对将来在触摸屏上显示汉字有所帮助,——这是后话,暂且按下不提。
上面所说的字符当然是ASCII码了,这种编码学过计算机的人可能会很熟悉,就不多讲了。本文所述者,就是通过ASCII码字库文件(网上有下载)和上面提到的其中之一的文件在终端上显示ASCII字符。这也为研究汉字显示打下基础(此为假话,写文章及做作所需也,实际上本文作者是先研究汉字显示再研究ASCII码显示的,勿模仿,切切)。
本文中使用的字库是8x16,每个字符占用内存空间为16字节(后面不再提及),文中显示字符所做工作有下面几步(显示汉字也差不多,当然,这又是后话 ):
1、打开字库文件(似乎是废话);
2、计算出要打印的字符在字库文件中的偏移量(亦即网上所说的“寻址”);
3、将这个偏移地址的数据(16字节)读取缓冲区中;
4、显示这个缓冲区,为1者打印,为0者留空。(由于是在终端上显示,因此,可以使用如“#”、“*”等字符代替。)
先说说如何找到某个字符在字库文件中的偏移量。字库里的字符排序是符合ASCII码顺序的,而每个字符占16字节,因此,将要打印的字符与16相乘,便得到这个字符在字库中的位置了,这个位置之后的16个字节的数据,就是这个字符了。比如大写字母“A”,十六进制是0x41(“A”还是十进制的65、八进制的101,其实都是一回事),它与0x10(十进制的16)相乘,得到0x410(计算机中使用十六进制十分方便,对于某些试题要求计算出某地址的十进制,我表示不理解,既不直观,又不方便,还容易出错)。使用十六进制编辑器(用UE相信会更方便些)查看这个偏移量,会发现有如下数据:
$ hexdump -C ../HZK/ASC16 | grep 410
00000410 00 00 10 38 6c c6 c6 fe c6 c6 c6 c6 00 00 00 00 |...8l...........|
00000410 00 00 10 38 6c c6 c6 fe c6 c6 c6 c6 00 00 00 00 |...8l...........|
所谓的字库,里面就是一些二进制数据。我们看看上面得到“A”的16个字节数据显示效果是怎样的。将上面的十六进制数据按二进制输出,一个字节一行,共占16行,如下:
00000000
00000000
00010000
00111000
01101100
11000110
11000110
11111110
11000110
11000110
11000110
11000110
00000000
00000000
00000000
00000000
00000000
00010000
00111000
01101100
11000110
11000110
11111110
11000110
11000110
11000110
11000110
00000000
00000000
00000000
00000000
不直观,将出现0的地方留空,则变成如下形状(受网页影响,为美观起见进行调整,虽非实际中二进制所示,但形状是一致的):
1
111
11 11
11 11
11 11
1111111
11 11
11 11
11 11
11 11
上面的便是传说中的“A”了。
下面就用代码读取字库信息,并显示出来。完整代码如下:
/***************************************************
源代码文件编码:ANSI
测试环境编码:zh_CN.UTF-8、zh_CN.gd2312
* ************************************************/
#include<stdio.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#ifdef DEBUG
#define debug(fmt, ...) printf(fmt, #__VA_ARGS__)
#else
#define debug(fmt, ...)
#endif
void display_font_ascii(char *asc)
{
int i, j;
debug("=================/n");
;i<;i++)
{
;j<;j++)
{
/* 逐位相与,为1者打印“*”,否则打印空格 */
if(asc[i] & (0x80>>j))
printf("*");
else
printf(" ");
}
printf("/n");
}
debug("=================/n");
}
int main()
{
int i;
unsigned char *p;
unsigned char asciicode[] = "Ab";
unsigned long offset;
FILE *asc;
];
if((asc=fopen("ASC16","rb"))==NULL)
{
perror("Can't Open ASC16");
exit();
}
/* ASCII字库文件 */
; i < ; i++)
{
debug("%c %x/n", asciicode[i], asciicode[i]); /* 打印数值 */
offset = asciicode[i]*;
debug("offset: %x/n", offset); /* 打印偏移量 */
fseek(asc,offset,SEEK_SET);
fread(ascii,,,asc); /* 读取16字节 */
display_font_ascii(ascii); /* 显示 */
}
fclose(asc);
;
}
源代码文件编码:ANSI
测试环境编码:zh_CN.UTF-8、zh_CN.gd2312
* ************************************************/
#include<stdio.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#ifdef DEBUG
#define debug(fmt, ...) printf(fmt, #__VA_ARGS__)
#else
#define debug(fmt, ...)
#endif
void display_font_ascii(char *asc)
{
int i, j;
debug("=================/n");
;i<;i++)
{
;j<;j++)
{
/* 逐位相与,为1者打印“*”,否则打印空格 */
if(asc[i] & (0x80>>j))
printf("*");
else
printf(" ");
}
printf("/n");
}
debug("=================/n");
}
int main()
{
int i;
unsigned char *p;
unsigned char asciicode[] = "Ab";
unsigned long offset;
FILE *asc;
];
if((asc=fopen("ASC16","rb"))==NULL)
{
perror("Can't Open ASC16");
exit();
}
/* ASCII字库文件 */
; i < ; i++)
{
debug("%c %x/n", asciicode[i], asciicode[i]); /* 打印数值 */
offset = asciicode[i]*;
debug("offset: %x/n", offset); /* 打印偏移量 */
fseek(asc,offset,SEEK_SET);
fread(ascii,,,asc); /* 读取16字节 */
display_font_ascii(ascii); /* 显示 */
}
fclose(asc);
;
}
由于是示意性代码,不必纠结于代码优化、代码风格等等问题。效果(同样进行了调整)如下:
$ ./a.out
*
***
** **
** **
** **
*******
** **
** **
** **
** **
***
**
**
****
** **
** **
** **
** **
** **
*****
*
***
** **
** **
** **
*******
** **
** **
** **
** **
***
**
**
****
** **
** **
** **
** **
** **
*****
还有另一种方法,不读取字库,而是将所有的字符存放于某个数组中,比如像font_8x16.c文件中的fontdata_8x16数组。这里假设数组为ascii_code,那么,上面的代码只需修改一小部分,如下:
for (i = 0; i < sizeof(asciicode)-1; i++)
{
debug("%c %x/n", asciicode[i], asciicode[i]);
offset = asciicode[i]* 16; // 寻址
p = ascii_code+offset; // 查找字符在ascii_code数组中的偏移量
debug("offset: %x/n", offset);
display_font_ascii(p);
}
{
debug("%c %x/n", asciicode[i], asciicode[i]);
offset = asciicode[i]* 16; // 寻址
p = ascii_code+offset; // 查找字符在ascii_code数组中的偏移量
debug("offset: %x/n", offset);
display_font_ascii(p);
}
效果是一样的,所不同的是,前一种方法需要读取字库文件,后一种方法直接在内存中读取,完整的ASCII码占用空间为4KB。不过,ASCII中可打印的字符共96个(经过认真数那些可打印的字符,发现实际上是95个(即32号到126号),这里将127号算上了,具体的请搜索一下ASCII),占用空间为96*16,即1536字节。由于前面0x20(即32)个字符在显示中没有用到,因此是可以去掉的,这样一来,寻址方式又稍有不同,如下:
for (i = 0; i < sizeof(asciicode)-1; i++)
{
debug("%c %x/n", asciicode[i], asciicode[i]);
offset = (asciicode[i] - 0x20 )* 16; // 寻址
p = ascii_code+offset; // 查找字符在ascii_code数组中的偏移量
debug("offset: %x/n", offset);
display_font_ascii(p);
}
{
debug("%c %x/n", asciicode[i], asciicode[i]);
offset = (asciicode[i] - 0x20 )* 16; // 寻址
p = ascii_code+offset; // 查找字符在ascii_code数组中的偏移量
debug("offset: %x/n", offset);
display_font_ascii(p);
}
由于文中涉及知识、代码、字库文件都可以在网络上找到,因此文中就不提供下载了。
资料:
1、ASCII码字库,有多种形式,如8*16、8*12,名称为ASC12、ASC16,搜索一下就能找到。
2、“字库数组”,可以在Linux内核源代码的./drivers/video/console目录下找到很多相关代码文件,如font_8x8.c、font_8x16.c、font_sun8x16.c等等。像font_8x16.c文件,代码中说是由cpi2fnt这个东西产生的,但搜索一下,没什么介绍,英文的又不想去看,对这个东西也就不了解了。
3、可以搜索一下字库生成工具,用这些工具可以生成需要的字库。






