内存映射写图和目录IO

tech2024-06-04  68

一、现在,我们能够正常显示一张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: 提示以及项目错误判断。  -> 用刷图来代替。(选做)

 

最新回复(0)