点阵字体显示系列之三:使用ncurses显示汉字

ncurses这个库,最早听说应该是当年刚接触Linux的时候,当时,我们宿舍就一个人在鼓捣Linux,他是我们后来的班长,如今在ZLG混,也不知混得怎么样了。我也不知道哪条神经线路出现故障了,竟然傻乎乎去学Linux,到后来,一发不可收拾,从此走上了一条不归路。到毕业前,我曾经说过了研究ncurses库的,还写了文章,文章说要在毕业时将ncurses掌握到什么程度。可惜,人算不如天算,计划跟不上变化,天有不测之风云,后来就不了了之了。

ncurses库已经离我们越来越远了,很多场合已经用不上了,特别是现在iphone、ipad、android横行的时代。
现在,趁着研究汉字显示的时候,用一下它吧。其实,最主要的原因是前面的代码只能竖着显示汉字,不能横着显示,因此看不到一连串汉字的显示效果。虽然没有兑现当时的诺言,不过还是没有忘记它(这话听起来怎么有点像那什么似的)。
以一个最简单的代码示例欢迎ncurses库:

1
2
3
4
5
6
7
8
9
10
11
12
13
/* hello.c */
#include <ncurses.h>

int main()
{       
        initscr();                /* 初始化,进入NCURSES模式 */
        mvprintw(10,1,"ABCD");
        refresh();                /* 将虚拟屏幕上的内容写到显示器上,并刷新*/
        mvprintw(11,1,"ABCD");
        refresh();
        endwin();                /* 退出NCURSES模式 */
        return 0;
}

编译命令如下:

1
$ gcc hello.c -lncurses

因为ncurses不是Linux标准库,因此需要使用-l指定这个库。
这个代码运行的效果是在屏幕的第10行和11行的第1列显示“ABCD”。 本文就是利用ncurses库,在指定的坐标打印字符。在原来基础上进行的修改不多,只需添加几个与ncurses有关的函数即可,比如初始化屏幕的,结束ncurses屏幕,刷新,等等。
下面是初步完成粗糙的新鲜出炉的代码,解决了前面文章中提到的中英文混合显示的bug。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*************************************************** 
 字符集编码统一为gb2312,即源代码文件保存格式为gb2312(notepad++下显示为“ANSI”),
 编译环境的字符集编码为gb2312,如果不是,可能得不到预期效果
               多个汉字 & 中英文混合显示
 
 编译:$ gcc font-test-ncurses.c -lncurses
 运行:$ ./a.out
 源代码文件编码:ANSI
 测试环境编码:zh_CN.UTF-8、zh_CN.gd2312

 log:
 中文使用字库HZK16,英文使用font_8x16.h头文件中的数组,寻址方式为a[i]*16,与ASCII字库一样
 而font_8x16-me.h头文件的数组,寻址方式为(a[i] - 0x20) * 16。
 
 *16与<<4暂时未作比较。
 *************************************************/

#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>
#include <ncurses.h>  /* ncurses库头文件 */

//#include "font_8x16.h"
#include "font_8x16-me.h"

#define font_size 32
#define ascii_size 16
//#define ascii_code fontdata_8x16 /* font_8x16.h */
#define ascii_code fontdata_8x16_me

/* for debug */
//#define DEBUG
#ifdef DEBUG
#define debug(fmt, ...) printw(fmt, ##__VA_ARGS__)
#else
#define debug(fmt, ...)
#endif

void display_font(int y, int x, unsigned char *mat)
{
        int i, j, k;
        for(i=0;i<16;i++)
        {
                for(j=0;j<2;j++)  /* 汉字占2个字节 */
                {
                        for(k=0;k<8;k++)         /* 输出一个字节*/
                        {
                                 /* 逐位相与,为1者,输出“*” */
                                if(mat[i*2+j] & (0x80>>k))
                                        mvprintw(y+i, x+j*8+k, "*");
                                else
                                        mvprintw(y+i, x+j*8+k, " ");
                        }
                }
        }
        refresh();
}

void display_font_ascii(int y, int x, unsigned char *ascii)
{
        int i, j;
        debug("=================/n");
        for(i=0;i<16;i++)
        {
                for(j=0;j<8;j++)
                {
                        if(ascii[i] & (0x80>>j))
                                mvprintw(y+i, x+j, "*");
                        else
                                mvprintw(y+i, x+j, " ");
                }
        }
        debug("=================/n");
        refresh();
}

int main()
{
        int i;
        //unsigned char incode[] = "我Az你个pf"; /* 全部是中文字符,英文为全角状态下输入 */
        //unsigned char incode[] = "人生如梦"; /* 全部中文 */
        //unsigned char incode[] = "I'm Late Lee"; /* 全部英文 */
        unsigned char incode[] = "我A你个BCD";        /* 中文、英文 */
        unsigned char *p;
        unsigned char *p_ascii;
        int qh,wh;
        unsigned long offset;
        FILE *HZK;
        unsigned char mat[32]={0};

        int y = 5;   /* 行 */
        int x = 0;   /* 列 */

        initscr();         /* init screen */

        if((HZK=fopen("HZK16","rb"))==NULL) {
                perror("Can't Open hzk16");
                exit(0);
        }
         /* 中英混合显示 */
        #if 0
         /* 中英文混合显示时,这种方法似乎不好处理,暂时舍弃 */
        for (i = 0; i < sizeof(incode)-1; i+=2)
        {
                qh = incode[i]   - 0xa0;
                wh = incode[i+1] - 0xa0;
                if (qh > 0 && wh > 0) {
                        debug("code : %x %x/n", incode[i], incode[i+1]);
                        offset = ( 94*(qh-1) + (wh-1) ) * 32;        // 计算偏移
                        fseek(HZK,offset,SEEK_SET);
                        fread(mat,32,1,HZK);
                        display_font(y, x, mat);
                        x += 16;
                }
                else {
                        int offset1, offset2;
                        offset1 = (incode[i]-0x20) * 16;  /*16*/
                        offset2 = (incode[i+1]-0x20) * 16;  /*16*/
                        p_ascii = ascii_code + offset1;
                        display_font_ascii(y, x, p_ascii);
                        x += 8;
                        p_ascii = ascii_code + offset2;
                        display_font_ascii(y, x, p_ascii);
                        x += 8;
                }
        }
        #endif
         /* 另一种方法 */
        #if 01
        p = incode;
        while (*p != 0)
        {
                qh = *p   - 0xa0;
                wh = *(p+1) - 0xa0;
                if (qh > 0 && wh > 0){
                        debug("code : %x %x/n", *p, *(p+1));
                        offset = ( 94*(qh-1) + (wh-1) ) * 32;
                        debug("qh: %x wh: %x offset: %x/n", qh, wh, offset);
                        fseek(HZK,offset,SEEK_SET);
                        fread(mat,32,1,HZK);
                        display_font(y, x, mat);
                        x += 16;
                        p+=2;         /* 中文字符,移动2个字节 */
                }
                else {
                        int offset1;
                        offset1 = (*p - 0x20 ) * 16;
                        p_ascii = ascii_code + offset1;
                        display_font_ascii(y, x, p_ascii);
                        x += 8;
                        p+=1;         /* 英文字符,移动1个字节 */
                }
        }
        #endif

        fclose(HZK);
        getch();    /*暂停*/
        endwin();  /* close it */
        return 0;
}

中文显示效果如下((“人生如梦”,为方便网页显示,非实际数据,下同):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

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

中英文混合显示效果如下(“我A你个BCD”):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
          *    *                                    *      *                              *
        ***  *  *                                *      *                              *
  ****      *    *              *              *      *                            *  *              ******        ****    *****
        *      *    *            ***          *      ********              *      *              **    **    **    **    **  **
        *      *        *      **  **        *      *            *            *          *            **    **  **        *    **    **
***************  **      **    **    *            *            *      *      *          **    **  **              **    **
        *      *              **      **  *  *  *        *                *        *        ***    *****    **              **    **
        *      *    *        *******      *            *            **          *          *      **    **  **              **    **
        *  *  *    *        **      **      *        *  *  *                      *                  **    **  **              **    **
        **      **          **      **      *        *  *    *                    *                  **    **  **        *    **    **
      **        *            **      **      *      *    *    **                  *                  **    **    **    **    **  **
  **  *      *  *          **      **      *    *      *      *                  *                ******        ****    *****
        *    *    *                              *            *                          *
        *  *        *  *                        *            *                          *
    *  *            *  *                        *        *  *                          *
      *                **                        *          *                            *

我一般不对代码进行详细注释和解释,对于看得懂的人,过多注释反而不好;对于看不懂的人,再多注释也是徒劳。不过,我还是凭良心在代码中写了一些注释。由于完整的工程代码还没有整理,未知bug还没找到,不敢放在网络上,见谅。由于各种网页行距、字与字之间间隔不太相同,如果文中效果显示不好,请移步这里查看:中文、英文字库显示效果
后记:
到本文为止,在PC机上的测试就完成了,已经取得了“决定性的进展”,下面就可以模仿tslib的代码,在触摸屏上显示汉字了,再接着就可以将触摸屏、ADC采集结合起来了。——未来总是美好的。
本次测试过程约使用了一周时间,完成了ADC驱动后,不知哪根神经线出了问题,也不知道怎么想的,突然想去研究汉字显示。结果也慢慢学到了点东西。从在脑中的计划,到实施,似乎一切都按我想像中的步骤有序地进行着:先学习编码,显示单个汉字,显示多个汉字,中英文混合显示,纵向显示,横向显示,等等。在未来的日子,我所遇之事是否也会按我的计划进行呢?我不得而知。《终结者3》中有一句经典台词,大意是说未来还没有定数,命运靠自己创造云云。我对社会也算有了初步认识,再说当年的激情、豪气,就有点显得不现实了(对于我所感兴趣的写代码事情,我依然有激情)。生活是要有一些激励的故事、人物的,但不会是我。我只想做普通人,在遇到问题时我会烦恼,解决问题后我会十分高兴,有研究心得时,愿意与他人分享。

无论怎么,未来总是美好的,一切都未知,值得我们憧憬。