1 框架
(1)需求分析
1)上电,LCD显示界面
2)根据配置文件,停留在当前界面,或者自动播放下一幅
3)点击,出现对话框,选择手动/自动播放
4)滑动:上——放大,下——缩小,左——上一幅,右——下一幅
5)左右移动速度较快,显示下下一幅
(2)包含进程
1)输入进程:
主控线程:得到事件,socket。
ts线程:使用tslib读触摸屏,封装上报。
按键线程:获取按键值。
2)显示进程
socket线程:上报事件
提前准备好的图片:上一幅、下一幅等。
(3)驱动:触摸屏、LCD,DMA,mmap
2 显示文字
2.1 文字编码方式
根据存储的数字找到对应的字符,并以相应的字体表示出来,即关注:数字–>字符–>字体。
(1)字符表示方法
1)ASCII
一个字节,只包含英文等少量字符。
2)GB2312
2个字节,与ASCII码兼容,一个小于127的字符意义与原来的相同,但是两个大于127的字符连在一起时,就表示一个汉字。
将字符进行分区处理,共有94个区,每个区有94个位。第一个字节代表区,第二个字节代表位。
以GB2312字符集的第一个汉字“啊”字为例,它的区号16,位号01,则区位码是1601,在大多数计算机程序中,高字节和低字节分别加0xA0得到程序的汉字处理编码0xB0A1。计算公式是:0xB0=0xA0+16,0xA1=0xA0+1。
3)BIG5
繁体字使用。
4)Unicode
将世界上所有的符号都纳入其中。
Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。常用UTF-8、UTF-16LE、UTF-16BE等表示。
由于存储的编码不同,所以显示的结果不同。编译时指定字符集:
-finput-charset=charset
-fexec-charset=charset
//例:gcc -finput-charset=GBK -fexec-charset=UTF-8 -o utf ansi.c
2.2 英文字母显示
2.2.1 概述
(1)点阵字符
英文字符点阵:在内核中,有font_8x16.c等文件描述了ASCII码字库的点阵。
汉字点阵:使用HZK16来表示汉字,HZK16是按分区表排列的点阵文件。对于一个汉字的位置,需要计算。
区码:区号(汉字的第一个字节)-0xa0 (因为汉字编码是从0xa0区开始的,所以文件最前面就是从0xa0区开始,要算出相对区码)
位码:位号(汉字的第二个字节)-0xa0
这样我们就可以得到汉字在HZK16中的绝对偏移位置:
offset=(94*(区码-1)+(位码-1))*32
(2)fbmem
mamp()函数:申请一段用户空间的内存区域,并映射到内核空间某个内存区域。
使用mamp申请一块内存映射到fb0文件,然后应用程序直接向内存写数据,即可直接写入fb0文件(显存地址)。
2.2.2 程序
功能:在LCD中间显示字符“A中”。
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#define FONTDATAMAX 4096
static const unsigned char fontdata_8x16[FONTDATAMAX] = {
//内容太多,与内核中fontdata_8x16中相同
};
int fd_fb;
struct fb_var_screeninfo var; /* Current var */
struct fb_fix_screeninfo fix; /* Current fix */
int screen_size;
unsigned char *fbmem;
unsigned int line_width;
unsigned int pixel_width;
int fd_hzk16;
struct stat hzk_stat;
unsigned char *hzkmem;
/* color : 0x00RRGGBB */
void lcd_put_pixel(int x, int y, unsigned int color)
{
unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width;
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)
{
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;
}
}
}
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++)
{
byte = dots[i];
for (b = 7; b >= 0; b--)
{
if (byte & (1<<b))
{
/* show */
lcd_put_pixel(x+7-b, y+i, 0xffffff); /* 白 */
}
else
{
/* hide */
lcd_put_pixel(x+7-b, y+i, 0); /* 黑 */
}
}
}
}
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++)
for (j = 0; j < 2; j++)
{
byte = dots[i*2 + j];
for (b = 7; b >=0; b--)
{
if (byte & (1<<b))
{
/* show */
lcd_put_pixel(x+j*8+7-b, y+i, 0xffffff); /* 白 */
}
else
{
/* hide */
lcd_put_pixel(x+j*8+7-b, y+i, 0); /* 黑 */
}
}
}
}
int main(int argc, char **argv)
{
unsigned char str[] = "中";
fd_fb = open("/dev/fb0", O_RDWR);
if (fd_fb < 0)
{
printf("can't open /dev/fb0\n");
return -1;
}
if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
{
printf("can't get var\n");
return -1;
}
if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix))
{
printf("can't get fix\n");
return -1;
}
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);
if (fbmem == (unsigned char *)-1)
{
printf("can't mmap\n");
return -1;
}
fd_hzk16 = open("HZK16", O_RDONLY);
if (fd_hzk16 < 0)
{
printf("can't open HZK16\n");
return -1;
}
if(fstat(fd_hzk16, &hzk_stat))
{
printf("can't get fstat\n");
return -1;
}
hzkmem = (unsigned char *)mmap(NULL , hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk16, 0);
if (hzkmem == (unsigned char *)-1)
{
printf("can't mmap for hzk16\n");
return -1;
}
/* 清屏: 全部设为黑色 */
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;
}
2.3 freetype理论
2.3.1 介绍
(1)矢量字体
问题:上面的测试程序,字体不能缩放。
使用矢量字体。矢量字体,记录关键点,例如
矢量字体:
1)存若干条闭合曲线的关键点。
2)使用数学曲线(贝塞尔曲线等方式)连接关键点。
3)填充内部空间。
(2)文字显示过程
1)给定文字,对于不同的编码,确定编码值
2)根据编码值,从字体文件中找到glyph
3)设置字体大小
4)用某些函数把glyph里的点缩放为字体大小
5)转换为位图点阵
6)显示出来
(3)Freetype
文字的存储由两部分组成,一部分是汉字的索引信息,一部分是汉字的字形(glyph)数据。查看freetype文档上述文字显示过程,对应程序:
1)初始化,FT_Init_Freetype
2)加载字体Face,从文件/内存,FT_New_Face
3)设置字体大小,FT_Set_Char_size、FT_Set_Pixel_Sizes
4)根据编码值,加载glyph
a.选择charmap:FT_Select_charmap
b.找到索引:glyph_index = FT_Get_char_Index
c.取出:FT_Load_Glyph
a\b\c等同FT_Load_Char
转成位图:FT_Render_Glyph
5)变换,移动、旋转……,
2.3.2 在PC上运行
在PC上安装
tar xjf freetype-2.4.10.tar.bz2 //解压
./config
make
sudo make install
找到freetype-doc-2.4.10.tar.bz2/freetype-2.4.10/docs/tutorial/example1.c。
修改example1.c显示大小。
#define WIDTH 80
#define HEIGHT 80
将C:/Windows/Fonts下的simsun.ttc(宋体)字体文件拷到虚拟机里,运行
gcc -o test example1.c -I/usr/local/include/freetype2 -lfreetype -lm
./test ./simsun.ttc abc
(1)问题1:中文+英文
上述程序问题:若是中文+英文,出错。
原因:中文和英文字符占用内存不同,字符串要为wchar_t类型。
(2)问题2:显示的中文和英文的对齐。
freetype给出的每个字符的显示示意图。
参考: /freetype-2.4.10/docs/reference/ft2-index.html,获取字符的坐标。
FT_Glyph_Get_CBox( FT_Glyph glyph, //该值通过FT_Get_Glyph()来获取
FT_UInt bbox_mode, //模式,填入FT_GLYPH_BBOX_TRUNCATE即可
FT_BBox *acbox ); //用来存放获取到的xMin, xMax, yMin, yMax信息
2.3.3 在LCD上运行
(1)安装freetype
#交叉编译:
tar xjf freetype-2.4.10.tar.bz2
./configure --host=arm-linux
make
make DESTDIR=$PWD/tmp install
#编译出来的头文件应该放入:
/usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include
#编译出来的库文件应该放入:
/usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib
#把tmp/usr/local/lib/* 复制到 /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib
sudo cp * /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib -d -rf
#nfs根文件系统下放入so链接文件
cp *so* /work/nfs_root/fs_mini_mdev_new/lib -d
#把tmp/usr/local/include/* 复制到 /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include
cp * /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include -rf
cd /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include
mv freetype2/freetype .
(2)在开发板上运行
在上次代码上修改。
得到的点阵在LCD上要做转换,因为LCD的坐标是:
void
draw_bitmap( FT_Bitmap* bitmap,
FT_Int x,
FT_Int y)
{
FT_Int i, j, p, q;
FT_Int x_max = x + bitmap->width;
FT_Int y_max = y + bitmap->rows;
//printf("x = %d, y = %d\n", x, y);
for ( i = x, p = 0; i < x_max; i++, p++ )
{
for ( j = y, q = 0; j < y_max; j++, q++ )
{
if ( i < 0 || j < 0 ||
i >= var.xres || j >= var.yres )
continue;
//image[j][i] |= bitmap->buffer[q * bitmap->width + p];
lcd_put_pixel(i, j, bitmap->buffer[q * bitmap->width + p]);
}
}
}
int main(int argc, char **argv)
{
unsigned char str[] = "中";
wchar_t *chinese_str = L"繁";
FT_Library library;
FT_Face face;
int error;
FT_Vector pen;
FT_GlyphSlot slot;
if (argc != 2)
{
printf("Usage : %s <font_file>\n", argv[0]);
return -1;
}
fd_fb = open("/dev/fb0", O_RDWR);
if (fd_fb < 0)
{
printf("can't open /dev/fb0\n");
return -1;
}
if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
{
printf("can't get var\n");
return -1;
}
if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix))
{
printf("can't get fix\n");
return -1;
}
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);
if (fbmem == (unsigned char *)-1)
{
printf("can't mmap\n");
return -1;
}
fd_hzk16 = open("HZK16", O_RDONLY);
if (fd_hzk16 < 0)
{
printf("can't open HZK16\n");
return -1;
}
if(fstat(fd_hzk16, &hzk_stat))
{
printf("can't get fstat\n");
return -1;
}
hzkmem = (unsigned char *)mmap(NULL , hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk16, 0);
if (hzkmem == (unsigned char *)-1)
{
printf("can't mmap for hzk16\n");
return -1;
}
/* 清屏: 全部设为黑色 */
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);
/* 显示矢量字体 */
error = FT_Init_FreeType( &library ); /* initialize library */
/* error handling omitted */
error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */
/* error handling omitted */
slot = face->glyph;
FT_Set_Pixel_Sizes(face, 24, 0);
/* 确定座标:
* lcd_x = var.xres/2 + 8 + 16
* lcd_y = var.yres/2 + 16
* 笛卡尔座标系:
* x = lcd_x = var.xres/2 + 8 + 16
* y = var.yres - lcd_y = var.yres/2 - 16
*/
pen.x = (var.xres/2 + 8 + 16) * 64;
pen.y = (var.yres/2 - 16) * 64;
/* set transformation */
FT_Set_Transform( face, 0, &pen);
/* load glyph image into the slot (erase previous one) */
error = FT_Load_Char( face, chinese_str[0], FT_LOAD_RENDER );
if (error)
{
printf("FT_Load_Char error\n");
return -1;
}
draw_bitmap( &slot->bitmap,
slot->bitmap_left,
var.yres - slot->bitmap_top);
return 0;
}
编译运行
arm-linux-gcc -finput-charset=GBK -o example1 example1.c -lfreetype -lm
./example1 ./simsun.ttc
(3) 显示多行文字
需要考虑显示的位置,防止重叠。
1)从左显示:先描画,算出边框
2)居中显示:算出需要显示内容的边框,再描画
通过FT_Glyph_Get_CBox获取位置。
typedef struct FT_BBox_
{
FT_Pos xMin, yMin;
FT_Pos xMax, yMax;
} FT_BBox;
(待续…)