网站建设与网页制作案例教程wordpress html5 模板下载
目录
一、LCD操作原理
二、涉及的 API 函数
1. open 函数
2. ioctl 函数
3. mmap 函数
二、Framebuffer函数分析
源码
1、打开设备
2、获取 LCD 参数
3、映射 Framebuffer
4、描点函数
5、随便画几个点
6、上机实验
三、FreeType编程
使用Freetype显示单个文字
1、矢量字体的引入
2、Freetype 介绍
3、 在 LCD 上显示一个矢量字体
一、LCD操作原理
在 Linux 系统中通过 Framebuffer 驱动程序来控制 LCD。Frame 是帧的意
思,buffer 是缓冲的意思,这意味着 Framebuffer 就是一块内存,里面保存着
一帧图像。Framebuffer 中保存着一帧图像的每一个像素颜色值,假设 LCD 的
分辨率是 1024x768,每一个像素的颜色用 32 位来表示,那么 Framebuffer 的
大小就是:1024x768x32/8=3145728 字节(一个字节有8位)。
介绍 LCD 的操作原理:
①驱动程序设置好 LCD 控制器:
根据 LCD 的参数设置 LCD 控制器的时序、信号极性;
根据 LCD 分辨率、BPP 分配 Framebuffer。
②APP 使用 ioctl 获得 LCD 分辨率、BPP
③APP 通过 mmap 映射 Framebuffer,在 Framebuffer 中写入数据
怎么找到点的地址呢?
假设需要设置 LCD 中坐标(x,y)处像素的颜色,首要要找到这个像素对应的
内存,然后根据它的 BPP 值设置颜色。假设 fb_base 是 APP 执行 mmap 后得到
的 Framebuffer 地址,如图所示:
可以用以下公式算出(x,y)坐标处像素对应的 Framebuffer 地址:
(x,y)像素起始地址=fb_base+(xres*bpp/8)y + xbpp/8
xres:一行有多少个像素
bpp:是像素的颜色怎么表示?它是用 RGB 三原色(红、绿、蓝)来表示的,在不同的 BPP 格式中,用不同的位来分别表示 R、G、B
对于 32BPP,一般只设置其中的低 24 位,高 8 位表示透明度,一般的 LCD都不支持。
对于 24BPP,硬件上为了方便处理,在 Framebuffer 中也是用 32 位来表示,效果跟 32BPP 是一样的。
对于 16BPP,常用的是 RGB565;很少的场合会用到 RGB555,这可以通过ioctl 读取驱动程序中的 RGB 位偏移来确定使用哪一种格式。
二、涉及的 API 函数
本节程序的目的是:打开 LCD 设备节点,获取分辨率等参数,映射Framebuffer,最后实现描点函数。
1. open 函数
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
可以看看我的这个博客:Linux文件编程
2. ioctl 函数
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
函数说明:
fd表示文件描述符;
request 表示与驱动程序交互的命令,用不同的命令控制驱动程序输出我们
需要的数据;
FBIOGET_VSCREENINFO具体这两个参数的使用下面会提及
FBIOPUT_VSCREENINFO
… 表示可变参数 arg,根据 request 命令,设备驱动程序返回输出的数据。
返回值:打开成功返回文件描述符,失败将返回-1。
ioctl 的作用非常强大、灵活。不同的驱动程序内部会实现不同的 ioctl,
APP 可以使用各种 ioctl 跟驱动程序交互:可以传数据给驱动程序,也可以从驱
动程序中读出数据。
3. mmap 函数
#include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
函数说明:
addr 表示指定映射的內存起始地址,通常设为 NULL 表示让系统自动选定
地址,并在成功映射后返回该地址;
length 表示将文件中多大的内容映射到内存中;
prot 表示映射区域的保护方式,可以为以下 4 种方式的组合
◼ PROT_EXEC 映射区域可被执行
◼ PROT_READ 映射区域可被读出
◼ PROT_WRITE 映射区域可被写入
◼ PROT_NONE 映射区域不能存取
Flags 表示影响映射区域的不同特性,常用的有以下两种
◼ MAP_SHARED 表示对映射区域写入的数据会复制回文件内,原来的文
件会改变。
◼ MAP_PRIVATE 表示对映射区域的操作会产生一个映射文件的复制,对
此区域的任何修改都不会写回原来的文件内容中。
返回值:若成功映射,将返回指向映射的区域的指针,失败将返回-1。
二、Framebuffer函数分析
源码
#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>
#include <sys/ioctl.h>static int fd_fb;
static struct fb_var_screeninfo var; /* Current var */
static int screen_size;
static unsigned char *fb_base;
static unsigned int line_width;
static unsigned int pixel_width;/*********************************************************************** 函数名称: lcd_put_pixel* 功能描述: 在LCD指定位置上输出指定颜色(描点)* 输入参数: x坐标,y坐标,颜色* 输出参数: 无* 返 回 值: 会* 修改日期 版本号 修改人 修改内容* -----------------------------------------------* 2020/05/12 V1.0 zh(angenao) 创建***********************************************************************/
void lcd_put_pixel(int x, int y, unsigned int color)
{unsigned char *pen_8 = fb_base+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;}}
}int main(int argc, char **argv)
{int i;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;}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;fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);if (fb_base == (unsigned char *)-1){printf("can't mmap\\n");return -1;}/* 清屏: 全部设为白色 */memset(fb_base, 0xff, screen_size);/* 随便设置出100个为红色 */for (i = 0; i < 100; i++)lcd_put_pixel(var.xres/2+i, var.yres/2, 0xFF0000);munmap(fb_base , screen_size);close(fd_fb);return 0;
}
1、打开设备
fd_fb = open("/dev/fb0", O_RDWR);
if (fd_fb < 0)
{
printf("can't open /dev/fb0\n");
return -1;
}
2、获取 LCD 参数
LCD 驱动程序给 APP 提供 2 类参数:可变的参数 fb_var_screeninfo、固
定的参数 fb_fix_screeninfo。编写应用程序时主要关心可变参数,它的结构
体定义如下(#include <linux/fb.h>):
可以使用以下代码获取 fb_var_screeninfo:
12 static struct fb_var_screeninfo var; /* Current var */if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
{printf("can't get var\n");return -1;
}注意到 ioctl 里用的参数是:FBIOGET_VSCREENINFO,它表示 get var screen
info,获得屏幕的可变信息;当然也可以使用 FBIOPUT_VSCREENINFO 来调整这
些参数,但是很少用到。对于固定的参数 fb_fix_screeninfo,在应用编程中很少用到。它的结构体定义如下:
3、映射 Framebuffer
要映射一块内存,需要知道它的地址──这由驱动程序来设置,需要知道它
的大小──这由应用程序决定。代码如下:
第 88 行中,screen_size 是整个 Framebuffer 的大小;PROT_READ | PROT_WRITE 表示该区域可读、可写;MAP_SHARED 表示该区域是共享的,APP 写入数据时,会直达驱动程序,这个参数的更深刻理解可以参考后面驱动基础中讲到的 mmap 知识。
4、描点函数
1 void lcd_put_pixel(int x, int y, unsigned int color)
2 {
3 unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;
4 unsigned short *pen_16;
5 unsigned int *pen_32;
6
7 unsigned int red, green, blue;
8
9 pen_16 = (unsigned short *)pen_8;
10 pen_32 = (unsigned int *)pen_8;
11
12 switch (var.bits_per_pixel)
13 {
14 case 8:
15 {
16 *pen_8 = color;
17 break;
18 }
19 case 16:
20 {
21 /* 565 */
22 red = (color >> 16) & 0xff;
23 green = (color >> 8) & 0xff;
24 blue = (color >> 0) & 0xff;
25 color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
26 *pen_16 = color;
27 break;
28 }
29 case 32:
30 {
31 *pen_32 = color;
32 break;
33 }
34 default:
35 {
36 printf("can't surport %dbpp\n", var.bits_per_pixel);
37 break;
38 }
39 }
40 }
①第 1 行中传入的 color 表示颜色,它的格式永远是 0x00RRGGBB,即RGB888。当 LCD 是 16bpp 时,要把 color 变量中的 R、G、B 抽出来再合并成 RGB565 格式。
②第 3 行计算(x,y)坐标上像素对应的 Framebuffer 地址。
③第 16 行,对于 8bpp,color 就不再表示 RBG 三原色了,这涉及调色板的概念,color 是调色板的值。
④第 22~24 行,先从 color 变量中把 R、G、B 抽出来。
⑤第 25 行,把 red、green、blue 这三种 8 位颜色值,根据 RGB565 的格式,只保留 red 中的高 5 位、green 中的高 6 位、blue 中的高 5 位,组合成一个新的 16 位颜色值。
⑥第 26 行,把新的 16 位颜色值写入 Framebuffer。
⑦第 31 行,对于 32bpp,颜色格式跟 color 参数一致,可以直接写Framebuffer。
5、随便画几个点
/* 清屏: 全部设为白色 */
96 memset(fbmem, 0xff, screen_size);
97
98 /* 随便设置出 100 个为红色 */
99 for (i = 0; i < 100; i++)
100 lcd_put_pixel(var.xres/2+i, var.yres/2, 0xFF0000);
6、上机实验
在 Ubuntu 中编译程序,先设置交叉编译工具链,再执行以下命令:
arm-buildroot-linux-gnueabihf-gcc -o show_pixel show_pixel.c
然后在开发板上执行 show_pixel 程序。
三、FreeType编程
使用Freetype显示单个文字
1、矢量字体的引入
使用点阵字库显示英文字母、汉字时,大小固定,如果放大缩小则会模糊甚
至有锯齿出现,为了解决这个问题,引用矢量字体。
矢量字体形成分三步:
第1步 确定关键点,
第2步 使用数学曲线(贝塞尔曲线)连接头键点,
第3步 填充闭合区线内部空间。
什么是关键点?以字母“A”为例,它的的关键点如图 中的黄色所示:
再用数学曲线(比如贝塞尔曲线)将关键点都连接起来,得到一系列的封闭的曲线,
如图所示:
最后把封闭空间填满颜色,就显示出一个 A 字母,如图所示:
如果需要放大或者缩小字体,关键点的相对位置是不变的,只要数学曲线平
滑,字体就不会变形。
2、Freetype 介绍
Freetype 是开源的字体引擎库,它提供统一的接口来访问多种字体格式文件,从而实现矢量字体显示。我们只需要移植这个字体引擎,调用对应的 API 接口,提供字体文件,就可以让 freetype 库帮我们取出关键点、实现闭合曲线,填充颜色,达到显示矢量字体的目的。
关 键 点 (glyph) 存 在 字 体 文 件 中 , Windows 使 用 的 字 体 文 件c:\Windows\Fonts 目录下,扩展名为 TTF 的都是矢量字库,本次使用实验使用的是新宋字体 simsun.ttc。
给定一个字符,怎么在字体文件中找到它的关键点?
首先要确定该字符的编码值:比如 ASCII 码、GB2312 码、UNICODE 码。如果字体文件支持某种编码格式(charset),就可以使用这类编码值去找到该字符的关键点(glyph)。有些字体文件支持多种编码格式(charset),这在文件中被称为charmaps(注意:这个单词是复数,意味着可能支持多种 charset)。以 simsun.ttc 为例,该字体文件的格如下:头部含有 charmaps,可以使用某种编码值去 charmaps 中找到它对应的关键点。下图中的“A、B、中、国、韦”等只是 glyph 的示意图,表示关键点。
Charmaps 表示字符映射表,字体文件可能支持哪一些编码,GB2312、UNICODE、BIG5 或其他。如果字体文件支持该编码,使用编码值通过 charmap就可以找到对应的 glyph,一般而言都支持 ==UNICODE ==码。
有了以上基础,一个文字的显示过程可以概括如下:
3、 在 LCD 上显示一个矢量字体
一、使用 wchar_t 获得字符的 UNICODE 值
要显示一个字符,首先要确定它的编码值。常用的是 UNICODE 编码,在程序里使用这样的语句定义字符串时,str 中保存的要么是 GB2312 编码值,要么是UTF-8 格式的编码值,即使编译时使用“-fexec-charset=UTF-8”,str 中保存的也不是直接能使用的 UNICODE 值:
char *str = “中”;
如果想在代码中能直接使用 UNICODE 值,需要使用 wchar_t,宽字符,示例代码如下:
01 #include <stdio.h>
02 #include <string.h>
03 #include <wchar.h>
04
05 int main( int argc, char** argv)
06 {
07 wchar_t *chinese_str = L"中 gif";
08 unsigned int *p = (wchar_t *)chinese_str;
09 int i;
10
11 printf("sizeof(wchar_t) = %d, str's Uniocde: \n", (int)sizeof(wchar_t));
12 for (i = 0; i < wcslen(chinese_str); i++)
13 {
14 printf("0x%x ", p[i]);
15 }
16 printf("\n");
17
18 return 0;
19 }
}
UTF-8 格式保存 test_wchar.c,编译、测试命令如下:
每个 wchar_t 占据 4 字节,可执行程序里 wchar_t 中保存的就是字符的 UNICODE值。
注意:如果 test_wchar.c 是以 ANSI(GB2312)格式保存,那么需要使用以下命令来编译:
gcc -finput-charset=GB2312 -fexec-charset=UTF-8 -o test_wchar test_wchar.c
二、使用 freetype 得到位图
要使用 freetype 得到一个字符的位图,只需要 4 个步骤,代码先贴出来再分析:
①初始化 freetype 库
158 error = FT_Init_FreeType( &library ); /* initialize library /
②加载字体文件,保存在&face 中:
161 error = FT_New_Face( library, argv[1], 0, &face ); / create face object /
162 / error handling omitted /
163 slot = face->glyph;
第 163 行是从 face 中获得 FT_GlyphSlot,后面的代码中文字的位图就是保存在 FT_GlyphSlot 里。
③设置字体大小
165 FT_Set_Pixel_Sizes(face, font_size, 0);
④根据编码值得到位图
使用 FT_Load_Char 函数,就可以实现这 3 个功能:
⚫ 根据编码值获得 glyph_index:FT_Get_Char_Index
⚫ 根据 glyph_idex 取出 glyph:FT_Load_Glyph
⚫ 渲染出位图:FT_Render_Glyph
代码如下:
175 / load glyph image into the slot (erase previous one) */
176 error = FT_Load_Char( face, chinese_str[0], FT_LOAD_RENDER );
执 行 FT_Load_Char 之后,字符的位图被存在 slot->bitmap 里 , 即face->glyph->bitmap。
三、在屏幕上显示位图
位图里的数据格式是怎样的?参考 example1.c 的代码,可以得到图:
要在屏幕上显示出这些位图,并不复杂:
183 draw_bitmap( &slot->bitmap,
184 var.xres/2,
185 var.yres/2);
draw_bitmap 函数代码如下,由于位图中每一个像素用一个字节来表示,在0x00RRGGBB 的颜色格式中它只能表示蓝色,所以在 LCD 上显示出来的文字是蓝色的:
=
四、编译上机试验
五、详细代码
#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>
#include <math.h>
#include <wchar.h>
#include <sys/ioctl.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_Hint fd_fb;
struct fb_var_screeninfo var; /* Current var */
int screen_size;
unsigned char *fbmem;
unsigned int line_width;
unsigned int pixel_width;/*********************************************************************** 函数名称: lcd_put_pixel* 功能描述: 在LCD指定位置上输出指定颜色(描点)* 输入参数: x坐标,y坐标,颜色* 输出参数: 无* 返 回 值: 会* 修改日期 版本号 修改人 修改内容* -----------------------------------------------* 2020/05/12 V1.0 zh(angenao) 创建***********************************************************************/
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;}}
}/*********************************************************************** 函数名称: draw_bitmap* 功能描述: 根据bitmap位图,在LCD指定位置显示汉字* 输入参数: x坐标,y坐标,位图指针* 输出参数: 无* 返 回 值: 无* 修改日期 版本号 修改人 修改内容* -----------------------------------------------* 2020/05/12 V1.0 zh(angenao) 创建***********************************************************************/
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 ( j = y, q = 0; j < y_max; j++, q++ ){for ( i = x, p = 0; i < x_max; i++, p++ ){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)
{wchar_t *chinese_str = L"繁";FT_Library library;FT_Face face;int error;FT_Vector pen;FT_GlyphSlot slot;int font_size = 24;if (argc < 2){printf("Usage : %s <font_file> [font_size]\n", argv[0]);return -1;}if (argc == 3)font_size = strtoul(argv[2], NULL, 0);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;}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;}/* 清屏: 全部设为黑色 */memset(fbmem, 0, screen_size);/* 显示矢量字体 */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, font_size, 0);/* 确定座标:*///pen.x = 0;//pen.y = 0;/* 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,var.xres/2,var.yres/2);return 0;
}