bitmap(位图)
.bmp图片与其它图片的区别
jpg/jpeg/png:这些图片都是经过数据压缩后的图片;如果直接读取,并不是图片的原始数据; 必须要先解压再读取。文件较小。
bmp:不采用任何压缩,存储的是图片原始数据;可以直接读取。文件较大。
大小(byte):800*480*3 + 54 = 1152000 + 54 = 1152054
说明:都出来的54个字节是bmp图片的头文件(包含图片的大小,格式等等)
bmp图片一个像素点的大小 24bit(3个字节)
说明:lcd一个像素点的大小是32bit(4个字节)
bmp图片一个像素点的组成方式
LCD:ARGB
BMP:RGB(少了一个透明度)
BMP文件开头部分是BMP格式头,里面存放了RGB数据的尺寸、分辨率、色深等重要信息。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));
a. 4字节倍数行距
BMP图片文件的一个重要规则是,每行数据字节数必须是4的倍数,假设某BMP图片的分辨率是 65 × 200,也就是说宽是 65
像素,假设每个字节色深是24bits(即3字节),那么这张图片一行的实际数据量是 65×3=195
个字节,但195不是4的倍数,因此在每一行的末尾都会添加一个无效字节,将行距尺寸补到196个字节。
处理的原则很简单,首先根据具体图片的尺寸和色深等信息,计算出一行中会出现的无效字节的个数(0-3个字节),计算公式参考:
int pad = ((4-( width * bpp/8 ) % 4)) % 4;
在处理图像数据的时候,直接跳过这些无效字节就好了。
b. 上下颠倒
BMP图片中的RGB数据是上下颠倒的,因此文件数据中的最后一行是图像的最上面第一行。需要注意的是,上下是颠倒的,但是左右是正常的,因此在处理数据的时候不能从最后一个字节开始,而是从最末一行的首字节开始。
1、打开lcd fd_lcd=open("/dev/fb0",O_RDWR);
2、内存映射 int* addr = mmap(NULL,大小,读写权限,分享权限,文件描述符,0);
3、打开bmp图读取数据 fd_bmp=open("bmp_name",O_RDWR);
4、跳过bmp图的头节点lseek(fd_bmp,54,SEEK_SET);
5、读取bmp的图片有效数据 char buf[800*480*3],read(fd_bmp,buf,800*480*3)
6、数据读取 *(addr+(479-y)*800+x) = buf[(y*800+x)*3+0] //B | (buf[(y*800+x)*3+1]<<8) //G | (buf[(y*800+x)*3+2]<<16); //R
7、取消内存映射munmap(addr,800*480*3)
8、关闭文件 close(fd_lcd) close(fd_bmp)
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char** argv)
{
int fd_lcd,fd_bmp;
int* addr = NULL;
//1、打开lcd
fd_lcd=open("/dev/fb0",O_RDWR);
if(fd_lcd == -1)
{
perror("open lcd fail");
return -1;
}
//2、内存映射 int* addr = mmap(NULL,大小,读写权限,分享权限,文件描述符,0);
addr = mmap(NULL,800*480*4,PROT_WRITE|PROT_READ,MAP_SHARED,fd_lcd,0);
if(addr == NULL)
{
perror("mmap addr fail");
return -1;
}
//3、打开bmp图读取数据
fd_bmp=open("bmp_name",O_RDWR); //这里的bmp_name为bmp图片的名字
if(fd_bmp == -1)
{
perror("open bmp fail");
return -1;
}
//4、跳过bmp图的头节点
lseek(fd_bmp,54,SEEK_SET);
//5、读取bmp的图片有效数据
char buf[800*480*3] = {0}; //只存储图片的有效数据
read(fd_bmp,buf,800*480*3);
//6、数据读取 bmp图是从下到上读取,所以从最后一行开始读取
int x,y;
for(x = 0;x < 800;x++)
{
for(y = 0;y < 480;y++)
{
*(addr + (479 - x)*800 + y) = buf[(x * 800 + y)*3] | (buf[(x * 800 + y)*3 + 1] << 8) | (buf[(x * 800 + y) * 3 + 2] << 16) |
}
}
//7、取消内存映射
munmap(addr,800*480*3)
//8、关闭LCD文件
close(fd_lcd)
//9、关闭BMP文件
close(fd_bmp);
return 0;
}
//bmp图片的算法,不同显示方法
//方法一
for(x = 0;x < 480;x++)
{
for(z=0;z<=x;z++)
{
for(y = 0;y < 800;y++)
{
*(addr +(479-x)*800+y) = buf[(x*800+y)*3] | (buf[(x*800+y)*3+1] << 8) | (buf[(x*800+y)*3+2] << 16);
}
}
}
//方法二
for(y = 0;y < 800;y++)
{
for(z=0;z<=y;z++)
{
for(x = 0;x < 480;x++)
{
*(addr +(479-x)*800+y) = buf[(x*800+y)*3] | (buf[(x*800+y)*3+1] << 8) | (buf[(x*800+y)*3+2] << 16);
}
}
}
//方法三
for(y=799;y>=0;y--)
{
for(z=0;z<=y;z++)
for(x=479;x>=0;x--)
{
*(addr +(479-x)*800+y) = buf[(x*800+y)*3] | (buf[(x*800+y)*3+1] << 8) | (buf[(x*800+y)*3+2] << 16);
}
}
格式跟读取800*480大小的bmp图类似,只需要添加获取bmp照片宽度和高度的代码,任意大小的bmp图片可以自己挑选出现的位置,但是要注意非法访问的情况,容易造成段错误。任意大小bmp图片制作的时候一定要注意宽度是4的倍数,但实际上我们使用bmp照片较少,因为占用空间大,占用硬件成本。
实现的代码如下:
#include
#include
#include
#include
#include
#include
int main(int argc,char **argv)
{
//1、打开LCD
int fd_lcd = open("/dev/fb0",O_RDWR);
if(fd_lcd == -1)
{
perror("open lcd fail");
return -1;
}
//2、LCD的映射--效率要高于LCD的读写
int *addr = NULL;
addr = mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(addr == NULL)
{
perror("mmap fail");
return -1;
}
//3、读取一张bmp图
//制作照片时的宽度必须是4的倍数,否则要做像素点补偿
int fd_bmp = open("zhu400_200.bmp",O_RDWR);
if(fd_bmp == -1)
{
perror("open bmp fail");
return -1;
}
//4、读取任意bmp图片的分辨率(高度与宽度)
int height=0;//高度
int width=0;//宽度
lseek(fd_bmp,18,SEEK_SET);
read(fd_bmp,&width,4); //读取它的宽度,读完之后此时指针已近偏移到第22个字节的位置
read(fd_bmp,&height,4);
printf("width=%d height=%d\n",width,height);
//5、去除掉头54个字节
lseek(fd_bmp,54,SEEK_SET);
char buf[height*width*3]; //只存储图片的有效数据
read(fd_bmp,buf,height*width*3); //柔性数组不能初始化
//6、自己选择照片初始位置
int offset_x=100; //照片x轴的偏移位置
int offset_y=100; //照片y轴的偏移位置
//7、操作LCD这段映射内存空间
int x,y;
for(y=0;y