字符编码
计算机的所有信息都以二进制表示(二进制数字)
如:程序中puts(“Hello 你好”)
输出字符串信息,字符串在计算机里面保存为二进制数值48 65 6C 6C 57 C4E3 BAC3,计算机中传输的只是数字,显示为Hello 你好。
字符显示: 数字 -> 代表什么 ->显示为“什么”
字符编码 字体文件
字符编码:用什么数字表示哪个字符
ASCII码-1个字节
GBK码-2个字节 ->编码方式不同,导致相同数字代表不同的字符->unicode码
BIG5码
Unicode只是一个符号集,它只规定了符号和二进制代码的对应关系,却没有规定二进制代码应该如何存储。UTF-8是Unicode的实现方式之一(UTF-16LE,UTF-16BE),是一种可变长的编码方式。优点:使用1~4个字节表示一个符号,根据不同的符号而变化字节长度,节省了空间。EE BB BF开头,每个字符以n个1开头,编码容错性高。
UTF-8的编码规则很简单,只有二条:
1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
如:"abc中"以不同编码方式存储
ASSIC:61 62 63 D6D0
UTF-8:EF BB BF 61 62 63 E4 B8 AD
UTF-16BE(大端):FE FF 0061 0062 0063 4E2D
UTF-16LE(小端):FF FE 6100 6200 6300 2D4E
其中unicode码的“中”表示为E4 B8 AD,二进制为:
1110 0100,10 111000,10 101101
而这些剩下的组合起来表示unicode值:4E2D
字体文件:显示为什么,包含编码表和字体数据(根据编码表找到对应的字符点阵)。
源文件中字符串用不同的编码方式保存,会导致执行时打印结果不一样。
怎么解决?可以通过参数来设置,编译程序时,要指定字符集
man gcc , /charset 搜索到参数帮助
-finput-charset = charset 表示源文件的编码方式, 默认以UTF-8来解析 输入文件a.c中的字符串在电脑中保存的数值编码方式
-fexec-charset = charset 表示可执行程序里的字符以什么编码方式来表示,默认是UTF-8
gcc -o a a.c
gcc -finput-charset=GBK -fexec-charset=UTF-8 -o utf-8_2 ansi.c //指定输入字符集为GBK,指定输出字符集为UTF-8。
字体显示
注意:LCD的坐标系和笛卡尔坐标系是倒着的!坐标计算时要转换
int main(int argc, char **argv){
unsigned char str[] = "中";
fd_fb = open("/dev/fb0", O_RDWR); //打开设备
ioctl(fd_fb, FBIOGET_VSCREENINFO, &var); //获取LCD可变信息:x,y分辨率和bpp等
ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix; //获得LCD固定信息
line_width = var.xres * var.bits_per_pixel / 8;
pixel_width = var.bits_per_pixel / 8;
screen_size = var.xres * var.yres * var.bits_per_pixel / 8; //屏幕大小
fbmem = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);//参数:NULL,大小,可读可写,文件
fd_hzk16 = open("HZK16", O_RDONLY); //打开汉字库16
fstat(fd_hzk16, &hzk_stat); //获取文件统计信息
hzkmem = (unsigned char *)mmap(NULL , hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk16, 0); //映射文件,当做内存使用
memset(fbmem, 0, screen_size); /* 清屏: 全部设为黑色 */
lcd_put_ascii(var.xres/2, var.yres/2, 'A'); //在中间显示
printf("chinese code: %02x %02x\n", str[0], str[1]);
lcd_put_chinese(var.xres/2 + 8, var.yres/2, str);
return 0;
}
显示字母
void lcd_put_ascii(int x, int y, unsigned char c){
unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16]; //从点阵数组中找到点阵
int i, b;
unsigned char byte;
for (i = 0; i < 16; i++){ //显示8X16的点阵
byte = dots[i];
for (b = 7; b >= 0; b--){
if (byte & (1<<b))
lcd_put_pixel(x+7-b, y+i, 0xffffff); /* 白 *//* show */
else
lcd_put_pixel(x+7-b, y+i, 0); /* 黑 *//* hide */
}
}
}
显示汉字
HZK16字库里16×16的汉字一共需要256个点来显示,也就是说需要32个字节才能达到显示一个普通汉字的目的。一个GB2312汉字是由两个字节编码的,范围为A1A1~FEFE。
区码:区号(汉字的第一个字节)-0xa0 (因为汉字编码是从0xa0区开始的,所以文件最前面就是从0xa0区开始,要算出相对区码)
位码:位号(汉字的第二个字节)-0xa0
一个汉字占两个字节,前一个字节为该汉字的区号,后一个字节为该汉字的位号。每一个区有94个字符。
汉字在HZK16文件中的绝对偏移位置:offset=(94*(区码-1)+(位码-1))*32
void lcd_put_chinese(int x, int y, unsigned char *str){
unsigned int area = str[0] - 0xA1; //区码
unsigned int where = str[1] - 0xA1; //位码
unsigned char *dots = hzkmem + (area * 94 + where)*32; //字符在汉字库中位置
unsigned char byte;
int i, j, b;
for (i = 0; i < 16; i++) //16x16
for (j = 0; j < 2; j++){ //两个字节
byte = dots[i*2 + j];
for (b = 7; b >=0; b--){
if (byte & (1<<b))
lcd_put_pixel(x+j*8+7-b, y+i, 0xffffff); /* 白 *//* show */
else
lcd_put_pixel(x+j*8+7-b, y+i, 0); /* 黑 *//* hide */
}
}
}
}
像素显示
/* color : 0x00RRGGBB */
void lcd_put_pixel(int x, int y, unsigned int color){
unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width; //计算出(x,y)在fbmem中的位置
unsigned short *pen_16;
unsigned int *pen_32;
unsigned int red, green, blue;
pen_16 = (unsigned short *)pen_8;
pen_32 = (unsigned int *)pen_8;
switch (var.bits_per_pixel){ //lcd的bpp,
case 8:{
*pen_8 = color;
break;
}
case 16:{ /* 红绿蓝565 */
red = (color >> 16) & 0xff;
green = (color >> 8) & 0xff;
blue = (color >> 0) & 0xff;
color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
*pen_16 = color;
break;
}
case 32:{
*pen_32 = color;
break;
}
default:{
printf("can't surport %dbpp\n", var.bits_per_pixel);
break;
}
}
}