1. BMP图片的解析和显示
BMP是一种常见的图片存储格式,是由微软制定的一种无压缩的图片存储格式
bit map picture(位图) 的缩写
bmp存储的时候是把bmp图片中每一个像素点的每一个bit都直接存储到文件中(bmp图片是没有被压缩的)
关于“bmp”图片存储的具体格式,可以参考"维基百科"关于“bmp”图片,有几个简单的概念:
位深:每一个像素点占用的bit数量(RGB、ARGB...)
常见的图片一般是24位位数,一个像素点占用24个bit位,存储了RGB信息bmp文件格式:
2. 自己制作一张bmp图片
注意:把图片的后缀名修改为.bmp,是否可以呢?
肯定不可以
文件的名字和文件的实际内容没有关系(文件的名字是目录的内容信息)后缀名只是决定文件的打开方式
使用画图工具:
1.bmp
大小:300*300的24位位图
理论大小:300*300*3 = 270000个字节
实际大小:270,054
54byte? 在bmp文件的头部,使用了54个字节保存了bmp图片的一些属性信息,如:深度,宽度,高度,大小....BMP大小 = 像素点的颜色数据 + 54 byte
2.bmp
大小:299*300的24位位图(水平 * 垂直)
理论大小:299*300*3 + 54 = 269,154个字节
实际大小:270,054
why???
windows规定bmp图片中,图片一行的像素点大小必须是4的整数倍,如果不是4的整数倍,则填充为4的整数倍(不解决癞子,显示的图片会发生倾斜)
一行大小:299*3 = 897 =======>填充为900
大小:900*300+542.bmp
大小:300*299的24位位图(水平 * 垂直)
理论大小:299*300*3 + 54 = 269,154个字节
实际大小:269154
3. 把图片上传到开发板
在开发板设备上显示,则需要把图片上传到开发板
4. BMP图片的显示原理
从BMP图片文件中把所有的像素点的信息读取出来,解析出每一个点的颜色,把点描绘到指定的位置,就可以把图片显示出来了
步骤:
1. 初始化LCD屏幕(打开屏幕+映射)
2. 打开BMP图片
3. 读取BMP图片的像素信息并解析 <------------
4. 关闭BMP图片
5. 把解析后的数据显示到屏幕对应的位置 <------------6. 解映射
7. 关闭LCD屏幕bmp图片的存储是以小端模式存储的
存储的时候
(高字节)RGB(低字节)
BGR BGR BGR
第一个点 第二个点 第三个点 ......
读取出来之后,buf就保存了所有像素点的信息(可能存在填充字节)但是我们的LCD上面一个像素点的显示是AGRB
BGR------>ARGB
位运算(&, |, ~, <<, >>, ^....)图片显示到开发板上面其实是倒过来的
(54个字节中有一个位图高度,为负数就表示图片的像素是从第一行存储到最后一行--->从左至右,从上至下
为正数就表示图片的像素是从最后一行存储到第一行--->从左至右,从下至上)
很多图片存储的时候都是从下至上的,可以根据属性去判断
5. 练习
1. 显示一张bmp图片到开发板
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <unistd.h> int main(int argc, char *argv[]) { // 1. 初始化LCD屏幕(打开屏幕+映射) int fd = open("/dev/fb0", O_RDWR); if (-1 == fd) { perror("open lcd failed"); return -1; } // plcd 指向了映射后的内存区域 int *plcd = mmap(NULL, 800 * 480 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (plcd == MAP_FAILED) { perror("mmap failed"); close(fd); return -1; } // 2. 打开BMP图片 int fd_bmp = open(argv[1], O_RDONLY); if (-1 == fd_bmp) { perror("open bmp failed"); munmap(plcd, 800 * 480 *4); close(fd); return -1; } // 3. 读取BMP图片的像素信息并解析 // 小端模式 // 第一种方法: #if 0 lseek(fd_bmp, 2, SEEK_SET); char buf0[4] = {0}; int r_bmp = read(fd, buf0, 4); if (-1 == r_bmp) { perror("read size failed"); munmap(plcd, 800 * 480 *4); close(fd_bmp); close(fd); return -1; } // BMP文件的大小(单位为字节) int size = buf0[3] << 24 | buf0[2] << 16 | buf0[1] << 8 | buf0[0]; lseek(fd_bmp, 0x12, SEEK_SET); r_bmp = read(fd_bmp, buf0, 4); if (-1 == r_bmp) { perror("read w failed"); munmap(plcd, 800 * 480 *4); close(fd_bmp); close(fd); return -1; } // 位图宽度 int w = buf0[3] << 24 | buf0[2] << 16 | buf0[1] << 8 | buf0[0]; r_bmp = read(fd_bmp, buf0, 4); if (-1 == r_bmp) { perror("read h failed"); munmap(plcd, 800 * 480 *4); close(fd_bmp); close(fd); return -1; } // 位图高度 int h = buf0[3] << 24 | buf0[2] << 16 | buf0[1] << 8 | buf0[0]; lseek(fd_bmp, 0x1c, SEEK_SET); r_bmp = read(fd_bmp, buf0, 2); if (-1 == r_bmp) { perror("read colour failed"); munmap(plcd, 800 * 480 *4); close(fd_bmp); close(fd); return -1; } // 位深 int colour_dep = buf0[1] << 8 | buf0[0]; lseek(fd_bmp, 0, SEEK_SET); char buf[800 * 480 * 4 + 54] = {0}; r_bmp = read(fd_bmp, buf, 800 * 480 * 4 + 54); if (-1 == r_bmp) { perror("read bmp failed"); munmap(plcd, 800 * 480 *4); close(fd_bmp); close(fd); return -1; } #endif // 第二种方法: #if 1 char buf[800 * 480 * 4 + 54] = {0}; int r_bmp = read(fd_bmp, buf, 800 * 480 * 4 + 54); if (-1 == r_bmp) { perror("read bmp failed"); munmap(plcd, 800 * 480 *4); close(fd_bmp); close(fd); return -1; } int size = buf[5] << 24 | buf[4] << 16 | buf[3] << 8 | buf[2]; int w = buf[21] << 24 | buf[20] << 16 | buf[19] << 8 | buf[18]; int h = buf[25] << 24 | buf[24] << 16 | buf[23] << 8 | buf[22]; int colour_dep = buf[29] << 8 | buf[28]; #endif // 大端模式 // int size, w, h; // lseek(fd, 2, SEEK_SET); // read(fd, &size, 4); // lseek(fd, 0x12, SEEK_SET); // read(fd, &w, 4); // read(fd, &h, 4); // lseek(fd, 0, SEEK_SET); // 4. 关闭BMP图片 close(fd_bmp); // 判断是否超出屏幕范围 if (w > lp->xres || h > lp->yres) { printf("brother,Out of range\n"); close(fd); return -1; } // 5. 把解析后的数据显示到屏幕对应的位置 int k = 54; unsigned char a, r, g, b; /* h<0:表示从上到下,从左到右存放数据 h>0:表示从下到上,从左到右存放数据 */ printf("w = %d, h = %d\n", w, h); for (int j = h - 1; j >= 0; j--) // 图片有h行 { // 显示当前行的每一个点 for (int i = 0; i < w; i++) // 每一行w个点 { // 解析每一个像素点 b = buf[k++]; g = buf[k++]; r = buf[k++]; a = (colour_dep == 32) ? buf[k++] : 0; int colour = (a << 24) | (r << 16) | (g << 8) | b; // 从(0, 0)开始 *(plcd + j * 800 + i) = colour; } // 每一行完成之后,处理赖子 if ((w * (colour_dep / 8)) % 4 != 0) { k = k + 4 - (w * (colour_dep / 8)) % 4; } } // 6. 解映射 munmap(plcd, 800 * 480 *4); // 7. 关闭LCD屏幕 close(fd); return 0; }