一、现在,我们能够正常显示一张800*480 24位bmp格式图片,但是是使用write写入数据到lcd设备上,速度比较慢,所以想办法写入数据时,不要使用write(),使用内存映射。
1、使用write()函数实现的代码。 可以显示24位,800*480像素点的图片。 int show_bmp(const char *bmp_path) { FILE *fp; int n,lcd; int i,j; int x,y; char bmp_buf[800*480*3] = {0}; char lcd_buf[800*480*4] = {0}; char show_buf[800*480*4] = {0}; /*1. 打开图片 */ fp = fopen(bmp_path,"r"); //默认在最开头 if(fp == NULL) printf("fopen error!\n"); /*2. 打开lcd设备 */ lcd = open("/dev/fb0",O_RDWR); if(lcd < 0) printf("open fb0 error!\n"); /*3. 先跳过54个头数据 */ fseek(fp,54,SEEK_SET); /*4. 将图片的数据读取到缓冲区中 */ n = fread(bmp_buf,800*480*3,1,fp); if(n!=1) printf("fread error!\n"); /*5. 将24位转32位 */ for(i=0,j=0;i<800*480*4;i+=4,j+=3) { lcd_buf[i] = bmp_buf[j]; lcd_buf[i+1] = bmp_buf[j+1]; lcd_buf[i+2] = bmp_buf[j+2]; lcd_buf[i+3] = 0; } /* 6. 上下颠倒 */ for(y=0;y<480;y++) { for(x=0;x<800*4;x++) { show_buf[800*4*y+x] = lcd_buf[800*4*(479-y)+x]; } }
//show_buf就是图片的正常内容。 /* 7. 将缓冲区的数据写入到屏幕上 */ n = write(lcd,show_buf,sizeof(show_buf)); if(n!=sizeof(show_buf)) printf("write error!\n"); /* 8. 关闭文件 */ fclose(fp); close(lcd); }
2、使用内存映射的思路。 1)分析到show_buf就是图片的正常内容。
show_buf[0] -> 第一个像素点的第一个字节 show_buf[1] -> 第一个像素点的第二个字节 show_buf[2] -> 第一个像素点的第三个字节 show_buf[3] -> 第一个像素点的第四个字节 show_buf[4] -> 第二个像素点的第一个字节 show_buf[5] -> 第二个像素点的第二个字节
2)产生一片内存空间,然后将像素点数据写入到内存中。 p = mmap(); memcpy(p);
3)自然lcd上就会有对应的颜色。
练习1: 使用内存映射的方式来完成显示图片,即完成mmap_show_bmp()函数。
int mmap_show_bmp(const char *bmp_path) { FILE *fp; int n,lcd; int i,j; int x,y; int k; char bmp_buf[800*480*3] = {0}; char lcd_buf[800*480*4] = {0}; char show_buf[800*480*4] = {0}; /*1. 打开图片 */ fp = fopen(bmp_path,"r"); //默认在最开头 if(fp == NULL) printf("fopen error!\n"); /*2. 打开lcd设备 */ lcd = open("/dev/fb0",O_RDWR); if(lcd < 0) printf("open fb0 error!\n"); /*3. 先跳过54个头数据 */ fseek(fp,54,SEEK_SET); /*4. 将图片的数据读取到缓冲区中 */ n = fread(bmp_buf,800*480*3,1,fp); if(n!=1) printf("fread error!\n"); /*5. 将24位转32位 */ for(i=0,j=0;i<800*480*4;i+=4,j+=3) { lcd_buf[i] = bmp_buf[j]; lcd_buf[i+1] = bmp_buf[j+1]; lcd_buf[i+2] = bmp_buf[j+2]; lcd_buf[i+3] = 0; } /* 6. 上下颠倒 */ for(y=0;y<480;y++) { for(x=0;x<800*4;x++) { show_buf[800*4*y+x] = lcd_buf[800*4*(479-y)+x]; } } /* 7. 产生一片内存空间,作为映射 */ char *p = (char *)mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd,0); if(p == (void *)-1) printf("mmap error!\n"); /* 8. 将数据拷贝到内存上 */ for(k=0;k<800*480*4;k++) { memcpy(p+k,&show_buf[k],1); } /* 7. 关闭文件 */ munmap(p,800*480*4); fclose(fp); close(lcd); return 0; }
练习2: 完成以下的函数。 show_all_bmp("./xx.bmp",200,200,300,200)
"./xx.bmp" -> 显示图片的路径。 200: 显示的起点的x轴坐标。 200: 显示的起点的y轴坐标。 300: 图片的宽度。 200: 图片的高度。
参考: 显示任意大小的图片.jpg
int show_all_bmp(const char *bmp_path,int s_x,int s_y,int wide,int high) { FILE *fp = NULL; int i,j; int x,y; int lcd; int k = 0; char *p = NULL; int x_max = (s_x+wide)*4; int y_max = s_y + high; char bmp_buf[wide*high*3]; char lcd_buf[wide*high*4]; char show_buf[wide*high*4]; //1. 先检查图片尺寸是否允许 if( (s_x+wide) > 800 || (s_y+high) > 480 ) { return -1; }
//2. 访问图片 fp = fopen(bmp_path,"r"); if(fp == NULL) printf("fopen error!\n"); //3. 跳过54个头数据 fseek(fp,54,SEEK_SET); //4. 将图片的内容读取出来 fread(bmp_buf,wide*high*3,1,fp); //5. 24位转32位 for(i=0,j=0;i<wide*high*4;i+=4,j+=3) { lcd_buf[i] = bmp_buf[j]; lcd_buf[i+1] = bmp_buf[j+1]; lcd_buf[i+2] = bmp_buf[j+2]; lcd_buf[i+3] = 0; } //6. 上下颠倒。 for(y=0;y<high;y++) { for(x=0;x<wide*4;x++) { show_buf[wide*4*y+x] = lcd_buf[wide*4*(high-1-y)+x]; } } //现在:show_buf就是小图片的正常数据 //7. 访问lcd设备 lcd = open("/dev/fb0",O_RDWR); if(lcd < 0) printf("open lcd error!\n"); //8. 内存映射 p = (char*)mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd,0); if(p == (void *)-1) printf("mmap error!\n"); //9.将show_buf的内容刷到内存上 for(y=s_y;y<y_max;y++) { for(x=s_x*4;x<x_max;x++) { memcpy(p+800*4*y+x,&show_buf[k],1); k++; } } //10. 撤销映射,然后关闭文件。 munmap(p,800*480*4); close(lcd); fclose(fp); return 0; }
二、目录IO函数接口。 1、学习目录IO的意义? 目录是存放数据的一种方式,如果有大量的文件,我们可考虑将文件存放在一个目录,这样去管理这个目录,就等价于管理所有的文件。
2、访问文件与访问目录有什么区别? 访问目录,可以得到该目录下的目录项,该目录项包含了文件的名字,文件的类型等.. 访问文件,可以得到该文件里面的字符。
3、目录IO中有哪些接口? 1)如何打开一个目录呢? -> opendir() -> man 3 opendir 功能: open a directory //打开一个目录 头文件: #include <sys/types.h> #include <dirent.h> -> 目录专属头文件 原型: DIR *opendir(const char *name);
参数: name:需要打开的那个目录的路径。 (绝对路径/相对路径)
返回值: 成功:目录流指针。 失败:NULL。
1)、什么是目录流指针? 打开一个目录,会返回一个指针,该指针默认指向目录中的第一项。
2)、问题一: 使用opendir来打开一个目录,就等价于切换到该目录下吗? -> 不是 3)、问题二: 要是目录下没有文件,那指向NULL吗? -> 不为NULL。
#include <sys/types.h> #include <dirent.h> #include <stdio.h> #include <stdlib.h>
int main(int argc,char *argv[]) { system("pwd"); DIR *dp = opendir("./ggy_dir"); if(dp == NULL) printf("opendir error!\n"); system("pwd"); return 0; }
2)如何切换到目录下? -> chdir() -> man 2 chdir (这里所说的切换,只是在程序中切换,ubuntu里面的路径不会变。)
功能:change working directory 头文件: #include <unistd.h>
原型: int chdir(const char *path);
参数: path:你想切换到那个目标的路径。
返回值: 成功:0 失败:-1
#include <sys/types.h> #include <dirent.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h>
int main(int argc,char *argv[]) { system("pwd"); DIR *dp = opendir("./ggy_dir"); if(dp == NULL) printf("opendir error!\n"); system("pwd"); int ret; ret = chdir("./ggy_dir"); if(ret == -1) printf("chdir error!\n"); system("pwd"); return 0; } 结果: gec@ubuntu:/mnt/hgfs/GZ2057/06 文件IO/06/code$ ./dir /mnt/hgfs/GZ2057/06 文件IO/06/code /mnt/hgfs/GZ2057/06 文件IO/06/code /mnt/hgfs/GZ2057/06 文件IO/06/code/ggy_dir
注意:切换到一个目录下的好处是什么。 如果不切换: fopen("./ggy_dir/ggy.txt");
如果切换: fopen("./ggy.txt");
3)如何读取目录下的内容? -> readdir() -> man 3 readdir 功能:read a directory 头文件: #include <dirent.h> 原型: struct dirent *readdir(DIR *dirp);
参数: dirp:目录流指针
返回值: 成功:结构体指针 struct dirent * 失败:NULL
struct dirent { ino_t d_ino; //索引号 off_t d_off; //偏移量 unsigned short d_reclen; //该项的长度 unsigned char d_type; //文件类型 char d_name[256]; //文件名 };
d_type: enum { DT_UNKNOWN = 0, -> 未知类型 DT_FIFO = 1, -> 管道文件 DT_CHR = 2, -> 字符设备文件 DT_DIR = 4, -> 目录文件 DT_BLK = 6, -> 块设备文件 DT_REG = 8, -> 普通文件 DT_LNK = 10, -> 链接文件 DT_SOCK = 12, -> 套接字文件 };
例题: 打开ggy_dir,并将里面的每一个目录项的类型与文件名读取出来。
#include <sys/types.h> #include <dirent.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h>
int main(int argc,char *argv[]) { DIR *dp = opendir("./ggy_dir"); if(dp == NULL) printf("opendir error!\n"); int ret; ret = chdir("./ggy_dir"); if(ret == -1) printf("chdir error!\n"); struct dirent *ep = NULL; while(1) { ep = readdir(dp); if(ep == NULL) { break; } if(ep->d_name[0] == '.') { continue; } printf("type = %d\n",ep->d_type); printf("name = %s\n",ep->d_name); printf("==========================\n"); } return 0; }
4)关闭目录。 -> closedir() -> man 3 closedir 功能: close a directory 头文件: #include <sys/types.h> #include <dirent.h>
原型: int closedir(DIR *dirp);
参数: dirp:目录流指针
返回值: 成功:0 失败:-1
5)重置目录流指针。 -> rewinddir() -> man 3 rewinddir 功能: reset directory stream //重置目录指针
头文件: #include <sys/types.h> #include <dirent.h>
原型: void rewinddir(DIR *dirp);
参数: dirp:目录流指针
返回值:无。
作业1: 把家长功能: 初始化老用户 撤掉。 程序运行后,会自动加载usr_data里面每一个用户的文件信息到数组中。
注意: chdir()后,注意fopen()那些文件的路径问题。
作业2: 把主界面 变成 图片,需要注册登录时,点击触摸屏来代替。 注册名字,密码 -> 还是用键盘输入。
作业3: 提示以及项目错误判断。 -> 用刷图来代替。(选做)