这时候有同学就会问,GEC6818是啥?不明白的同学可以去百度哈.....
为什么是显示bmp格式的图片呢?而不是常见的jpg格式的图片呢?
因为BMP格式是微软(MicroSoft)公司发明的数据封装格式,这种格式最大的特征是都没有任何的压缩,缺点文件尺寸都比较大,不适合在互联网上传播,所以在网上常见的是jpg这种经过压缩处理的图片,优点是数据读取出来即可使用,无需任何解码器支持。因此BMP格式文件内部存储的就是RGB数据,无需任何解码。
那怎么样才能将图片显示在屏幕上呢?
Linux下一切皆文件:
在Linux系统中,一个bmp格式的图片相当于一个存储着这张图片的RGB数据的文件,开发板的屏幕也相当于一个文件,而让bmp图片显示在屏幕上,不就是将图片文件的数据写在屏幕文件里面吗?就相当于抄作业、拷贝等类似的操作。
那怎么样把图片数据抄到屏幕上面呢?
一、打开bmp文件,利用open函数打开文件,不明白open函数怎么样,自行去查手册。
二、把bmp文件内容读取出来,用read函数读,那要读多少才合适呢?首先bmp图片是有一个个像素点组合而成的,一个像素点是3个字节,解析图如下,这个是从winhex软件看到的一个红色的bmp图片文件的数据,里面的RGB数据存放是反过来的实际上是BGR,文件的不全是存放着图片的RGB数据,在文件前54个字节里面存着图片高度宽度等信息。图中的蓝色框是RGB数据开始的地方,前面存的是图片的基本信息了。
读多少就得看前面文件头的图片信息了,在这里,我就不读了,自己定制一个800*480个像素大小的bmp图片,因为开发板的屏幕的分辨率就是800*480的,也就是屏幕的一行由800个像素点排列而成,宽度为480个像素点,而一个像素点由3个字节组成,所以要定义一个800*480*3个字节大小的数组例如
unsigned char bmpbuff[800*480*3] = {0};
三、怎么把读到的数据放到屏幕文件呢?
同样第一步就是打开屏幕文件。
到这里说一下屏幕的像素点是四个字节的,ARGB前面的A的位置一般是保留为0的,所以只用后面三个字节RGB,这样就要定义800*480*4个字节大小的数组,刚好int 是4个字节,可以这样子定义
unsigned int buff[800*480] = {0};
传统的抄作业是一个个字地抄,如果用write函数的话,系统也会像小学生抄作业一样,一个一个地抄,这样就有一个缺点,会很慢,导致屏幕显示图片地时候要一段时间才能把图片完全显示出来,现在用一种相当于直接抄一页作业的方法,就是映射,相当于一张复印纸,直接一拍在作业上就把作业的所有内容都刻在一页纸上面,到时候屏幕显示图片的时候,不是一个个地写,就直接把照片贴上去,这样显得速度快多了。下面是申请的方法
unsigned int mem_p = (unsigned int *)mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE, MAP_SHARED, lcd_fd, 0);
四、刚刚说到bmp图片的像素是3个字节的,而屏幕像素是4个字节的,虽然前面一个不用,但是还得要有的,而且bmp像素的RGB是反过来的,如图所示
所以就要把相对应的字节移位到字节相对应的位置,B位置不动,G左移1个字节就是8位,R左移2个字节就是16位,这样就能够刚刚好对应上屏幕像素点了。
buff[i] = bmpbuff[3*i+0] | bmpbuff[3*i+1]<<8 | bmpbuff[3*i+2]<<16;
五、把申请的映射和两个打开i的文件关闭就OK了
munmap(mem_p, 800*480*4);
close(lcd_fd);
close(bmp_fd);
六、实现理论
注意的是要想在6818板上面运行程序,要使用交叉编译工具链,因为开发板用的是arm架构的,所以要用对的编译器编译出来的程序才能正常运行。下面我用下面的程序显示我们麦麦的图片
//显示一张800*480的图片
#include <stdio.h>
#include <dlfcn.h> // 动态加载动态库的头文件:dlopen()、dlsym()
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/mman.h>
// BMP格式头规范
struct bitmap_header
{
int16_t type;
int32_t size; // 图像文件大小
int16_t reserved1;
int16_t reserved2;
int32_t offbits; // bmp图像数据偏移量
}__attribute__((packed));
struct bitmap_info
{
int32_t size; // 本结构大小
int32_t width; // 图像宽
int32_t height; // 图像高
int16_t planes;
int16_t bit_count; // 色深
int32_t compression;
int32_t size_img; // bmp数据大小,必须是4的整数倍
int32_t X_pel;
int32_t Y_pel;
int32_t clrused;
int32_t clrImportant;
}__attribute__((packed));
// 以下结构体不一定存在于BMP文件中,除非:
// bitmap_info.compression为真
struct rgb_quad
{
int8_t blue;
int8_t green;
int8_t red;
int8_t reserved;
}__attribute__((packed));
#define FB_FILE "/dev/fb0"
unsigned int *mem_p;
int lcd_fd;
int lcd_init(void);
int lcd_uninit(void);
int show_bmp(const char *pathname);
int main(void)
{
lcd_init();
show_bmp("a.bmp");
lcd_uninit();
return 0;
}
int lcd_init(void)
{
//打开file.txt文件, 文件不存在,则打开失败,如果使用O_CREAT,那必须要添加文件权限。
lcd_fd = open(FB_FILE, O_RDWR);
if(lcd_fd == -1)
{
printf("open a.txt fail\n");
return -1;
}
//屏幕映射
mem_p = (unsigned int *)mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE, MAP_SHARED, lcd_fd, 0);
if(mem_p == MAP_FAILED)
{
printf("mmap fail\n");
}
return 0;
}
int lcd_uninit(void)
{
// 解除映射
munmap(mem_p, 800*480*4);
close(lcd_fd);
}
int show_bmp(const char *pathname)
{
int i, j, x, y;
unsigned char bmpbuff[800*480*3] = {0};
unsigned int buff[800*480] = {0};
int bmp_fd = open(pathname, O_RDONLY);
if(bmp_fd == -1)
{
printf("open bmp fail\n");
return -1;
}
//跳过54个字节头
lseek(bmp_fd, 54, SEEK_SET);
read(bmp_fd, bmpbuff, sizeof(bmpbuff));
for(i=0; i<800*480; i++)
{
//buff[0] = bmpbuff[0]<<16 | bmpbuff[1]<<8 | bmpbuff[2];
buff[i] = bmpbuff[3*i+0] | bmpbuff[3*i+1]<<8 | bmpbuff[3*i+2]<<16;
}
//显示图片(倒置:反过来的)
for(y=0; y<480; y++)
{
for(x=0; x<800; x++)
{
*(mem_p+y*800+x) = buff[y*800+x];
}
}
close(bmp_fd);
return 0;
}
发现我们麦麦的图片反过来显示了,看规律发现只是上下的位置倒过来了,左右的位置没有发生变化,方法思路如图所示
以此类推,把上下行的数据倒过来放到屏幕文件里面进行了。
下面是倒置过来的全代码,可以看得出区别在哪里
//显示一张800*480的图片
#include <stdio.h>
#include <dlfcn.h> // 动态加载动态库的头文件:dlopen()、dlsym()
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/mman.h>
// BMP格式头规范
struct bitmap_header
{
int16_t type;
int32_t size; // 图像文件大小
int16_t reserved1;
int16_t reserved2;
int32_t offbits; // bmp图像数据偏移量
}__attribute__((packed));
struct bitmap_info
{
int32_t size; // 本结构大小
int32_t width; // 图像宽
int32_t height; // 图像高
int16_t planes;
int16_t bit_count; // 色深
int32_t compression;
int32_t size_img; // bmp数据大小,必须是4的整数倍
int32_t X_pel;
int32_t Y_pel;
int32_t clrused;
int32_t clrImportant;
}__attribute__((packed));
// 以下结构体不一定存在于BMP文件中,除非:
// bitmap_info.compression为真
struct rgb_quad
{
int8_t blue;
int8_t green;
int8_t red;
int8_t reserved;
}__attribute__((packed));
#define FB_FILE "/dev/fb0"
unsigned int *mem_p;
int lcd_fd;
int lcd_init(void);
int lcd_uninit(void);
int show_bmp(const char *pathname);
int main(void)
{
lcd_init();
show_bmp("a.bmp");
lcd_uninit();
return 0;
}
int lcd_init(void)
{
//打开file.txt文件, 文件不存在,则打开失败,如果使用O_CREAT,那必须要添加文件权限。
lcd_fd = open(FB_FILE, O_RDWR);
if(lcd_fd == -1)
{
printf("open a.txt fail\n");
return -1;
}
//屏幕映射
mem_p = (unsigned int *)mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE, MAP_SHARED, lcd_fd, 0);
if(mem_p == MAP_FAILED)
{
printf("mmap fail\n");
}
return 0;
}
int lcd_uninit(void)
{
// 解除映射
munmap(mem_p, 800*480*4);
close(lcd_fd);
}
int show_bmp(const char *pathname)
{
int i, j, x, y;
unsigned char bmpbuff[800*480*3] = {0};
unsigned int buff[800*480] = {0};
int bmp_fd = open(pathname, O_RDONLY);
if(bmp_fd == -1)
{
printf("open bmp fail\n");
return -1;
}
//跳过54个字节头
lseek(bmp_fd, 54, SEEK_SET);
read(bmp_fd, bmpbuff, sizeof(bmpbuff));
for(i=0; i<800*480; i++)
{
//buff[0] = bmpbuff[0]<<16 | bmpbuff[1]<<8 | bmpbuff[2];
buff[i] = bmpbuff[3*i+0] | bmpbuff[3*i+1]<<8 | bmpbuff[3*i+2]<<16;
}
//显示图片(倒置:反过来的)
// for(y=0; y<480; y++)
// {
// for(x=0; x<800; x++)
// {
// *(mem_p+y*800+x) = buff[y*800+x];
// }
// }
//正显示图片
for(y=0; y<480; y++)
{
for(x=0; x<800; x++)
{
*(mem_p+y*800+x) = buff[(479-y)*800+x];
}
}
close(bmp_fd);
return 0;
}
这样,我们的麦麦就正过来了
虽然这是一个很简单的知识,也很容易去实现,但是今天我才真正如此清晰其中的步骤,千里之行始于足下,共勉respect!下一个目标,学会怎么利用字库,将中文显示在屏幕上。