目录
一、24位bmp图片格式特点
54头字节,每个像素占3字节BGR顺序,上下颠倒存储,bmp图片的宽度占用的字节数如果不能被4整除,window系统会给每一行填充垃圾数凑够4字节整除。
液晶屏:每个像素点占4个字节,分别表ARGB的值, A表示透明度
54字节的头信息在另外一位大佬的博客中有详细的介绍,以下为链接:24位图片特点
二、具体实现思路
第一步:打开你要显示的图片,必须为bmp格式。 open()
第二步:打开开发板液晶的驱动。 open("/dev/fb0")
第三步:读取图片的像素,保存到buf中。(有效像素点的值在54字节后,使用lseek()移动光标) read()。
第四部:往液晶写像素,必须对应,否则图片失真。
最后关闭图像和液晶。
#include "myhead.h"
int main(int argc,char **argv)
{
int i;
int x,y;
int bmpfd;
int lcdfd;
//定义数组存放读取到的颜色值
char bmpbuf[800*480*3]; //BGR数据
//定义数组存放转换得到的ARGB数据
int lcdbuf[800*480];
//定义一个临时数组
int tempbuf[800*480];
//打开要显示的bmp图片
bmpfd=open(argv[1],O_RDWR);
if(bmpfd==-1)
{
perror("打开bmp失败!\n");
return -1;
}
//打开液晶屏的驱动
lcdfd=open("/dev/fb0",O_RDWR);
if(lcdfd==-1)
{
perror("打开lcd失败!\n");
return -1;
}
//跳过54字节的头信息,从55开始读取
lseek(bmpfd,54,SEEK_SET);
//从55字节开始读取
read(bmpfd,bmpbuf,800*480*3);
//把3个字节bmp数据--》转换成4个字节ARGB数据
/*
bmpbuf[0]-->R
bmpbuf[1]-->G
bmpbuf[2]-->B
*/
for(i=0; i<800*480; i++) //保证每个像素点都能转换
lcdbuf[i]=0x00<<24|bmpbuf[3*i+2]<<16|bmpbuf[3*i+1]<<8|bmpbuf[3*i];
//(x,y)这个点跟(x,479-y)调换 lcdbuf[y*800+x]
//把颠倒的图片翻转过来
for(x=0; x<800; x++)
for(y=0; y<480; y++)
//lcdbuf[(479-y)*800+x]=lcdbuf[y*800+x]; //错误,不应该自己赋值给自己
tempbuf[(479-y)*800+x]=lcdbuf[y*800+x];
//把转换得到的ARGB数据填充到液晶屏
write(lcdfd,tempbuf,800*480*4);
//关闭
close(bmpfd);
close(lcdfd);
return 0;
}
问题1:
write()显示图像效率太低,速度慢。
原因:
解决:#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
返回值:成功 返回液晶屏的首地址
失败 NULL
参数: addr --》一般设置为NULL 表示由操作系统自动分配硬件的首地址
length --》你想映射的液晶屏地址大小 800*480*4
prot --》内存区域的访问权限
PROT_READ //可读
PROT_WRITE //可写
flags --》你映射的地址空间是否可以被共享
MAP_SHARED 可以共享
MAP_PRIVATE 不可以共享
fd --》你要映射的硬件设备的文件描述符
offset --》地址偏移,一般设置为0
解除映射
int munmap(void *addr, size_t length);
参数:addr --》你之前映射得到的首地址
length --》映射的内存大小
问题2:
如何实现任意位置显示任意大小图像
#include "myhead.h"
int showbmp(int x,int y,char *bmpname);
int main(int argc,char **argv)
{
int x = atoi(argv[2]);//atoi()将字符型转换成整形
int y = atoi(argv[3]);
showbmp(x,y,argv[1]);
return 0;
}
int showbmp(int x,int y,char *bmpname)
{
int i,j,k=0;
int w,h;//图片的宽和高
int bmpfd;
int lcdfd;
int headbuf[2]={0};//headbuf[0] 宽 headbuf[1] 高
//定义数组存放转换得到的ARGB数据
int lcdbuf[800*480];
//打开要显示的bmp图片
bmpfd=open(bmpname,O_RDWR);
if(bmpfd==-1)
{
perror("打开bmp失败!\n");
return -1;
}
//打开液晶屏的驱动
lcdfd=open("/dev/fb0",O_RDWR);
if(lcdfd==-1)
{
perror("打开lcd失败!\n");
return -1;
}
//映射得到lcd的首地址
int *lcdmem=mmap(NULL,800*480*4, PROT_READ |PROT_WRITE,MAP_SHARED ,lcdfd,0 );
lseek(bmpfd,18,SEEK_SET);
//读头信息
read(bmpfd,headbuf,8);
w=headbuf[0];
h=headbuf[1];
if(x+w>800||y+h>480 ) //判断图像是否在合理范围内
{
perror("图片超出范围!\n");
return -1;
}
//定义数组存放读取到的颜色值
char bmpbuf[w*h*3]; //BGR数据
//定义一个临时数组
int tempbuf[w*h];
//跳过54字节的头信息,从55开始读取
lseek(bmpfd,28,SEEK_CUR);
if((3*w)%4==0)
{
read(bmpfd,bmpbuf,w*h*3); //从55字节开始读取
}else
{
for(int k=0;k<h;k++)
{
read(bmpfd,&bmpbuf[k*w*3],w*3);
lseek(bmpfd,4-(3*w)%4,SEEK_CUR );
}
}
for(i=0; i<w*h; i++) //保证每个像素点都能转换
lcdbuf[i]=0x00<<24|bmpbuf[3*i+2]<<16|bmpbuf[3*i+1]<<8|bmpbuf[3*i];
//把颠倒的图片翻转过来
for(i=0; i<w; i++)
for(int j=0; j<h; j++)
tempbuf[(h-j)*w+i]=lcdbuf[j*w+i];
//确定坐标
for(i=0;i<h;i++)
for(int j=0;j<w;j++)
{
*(lcdmem+800*(i+y)+j+x)=tempbuf[k];
k++;
}
//关闭
close(bmpfd);
close(lcdfd);
//解除映射
munmap(lcdmem,800*480*3);
return 0;
}